Astronomical pacing of the Lau biogeochemical Event captured in the Kosov Quarry section

A study on the imprint of astronomical cycles in the Kosov quarry spanning the Lau Event during the Silurian

Authors

Michiel Arts1

Anne-Christine da Silva1

1 - SEDICLIM lab, Department of Geology, University of Liege, Belgium

Published

October 1, 2026

This study is based onthe induration record extracted from the litholog of Fryda, Jiri & Lehnert, Oliver & Frýdová, Barbora & Farkas, Juraj & Kubajko, Michal. (2020). Carbon and sulfur cycling during the mid-Ludfordian anomaly and the linkage with the late Silurian Lau/Kozlowskii Bioevent. Palaeogeography, Palaeoclimatology, Palaeoecology. 564. doi:10.1016/j.palaeo.2020.110152

Code
setwd("D:/Phd/documents/kosov")

library(truncnorm)
library(DescTools)
library(rlist)
library(parallel)
library(foreach)
library(iterators)
library(doParallel)
library(astrochron)
library(ggplot2)
library(tidyr)
library(doSNOW)
library(progress)
library(tcltk)
library(matrixStats)
library(tidyr)
library(colorednoise)
library(stats)
library(WaveletComp)
library(reshape2)
library(viridis)
library(WaverideR)
library(raster)
library(jpeg)
library(httr)
library(gt)

#this function is needed to complete the wavelet tracking
completed_series <-   function (wavelet = NULL,
                                tracked_curve = NULL,
                                period_up = 1.2,
                                period_down = 0.8,
                                extrapolate = TRUE,
                                genplot = FALSE,
                                keep_editable = FALSE) {
  my.w <- wavelet
  my.data <- cbind(wavelet$x, wavelet$y)
  Pwert <- my.w$Power
  maxdetect <- matrix(nrow = (nrow(Pwert)), ncol = ncol(Pwert), 0)
  for (j in 1:ncol(Pwert)) {
    for (i in 2:(nrow(maxdetect) - 1)) {
      if ((Pwert[i, j] - Pwert[(i + 1), j] > 0) &
          (Pwert[i, j] - Pwert[(i - 1), j] > 0)) {
        maxdetect[i, j] <- 1
      }
    }
  }
  out <- tracked_curve
  out <- na.omit(out)
  yleft_out <- out[1, 2]
  yright_out <- out[nrow(out), 2]
  seq <- seq(
    from = min(my.data[, 1]),
    to = max(my.data[, 1]),
    by = my.data[, 1][2] - my.data[, 1][1]
  )
  app <- approx(
    x = out[, 1],
    y = out[, 2],
    xout = seq,
    method = "linear",
    yleft = yleft_out,
    yright = yright_out
  )
  app <- as.data.frame(cbind(app$x, app$y))
  periods <- as.data.frame(my.w$Period)
  completed_series <- matrix(data = NA,
                             nrow = nrow(app[, ]),
                             ncol = 2)
  completed_series[, 1] <- app[, 1]
  
  
  for (i in 1:nrow(completed_series)) {
    row_nr <- Closest(periods[, 1], app[i, 2], which = TRUE)
    row_nr
    row_nr <- row_nr[1]
    if (maxdetect[row_nr, i] == 1) {
      completed_series[i, 2] <- periods[row_nr, ]
    }
    else {
      sel_row <- as.data.frame(maxdetect[, i])
      sel_row$period <- periods[, 1]
      sel_row <- sel_row[sel_row[, 1] > 0, ]
      row_nr_sel_row <- Closest(sel_row[, 2], app[i, 2], which = TRUE)
      row_nr_closest <- Closest(periods[, 1], sel_row[row_nr_sel_row, 2], which = TRUE)
      closest_period <- periods[unlist(row_nr_closest[1]), 1]
      if ((closest_period < (app[i, 2] * period_up)) &
          (closest_period > (app[i, 2] * period_down))) {
        completed_series[i, 2] <- closest_period
      }
    }
  }
  if (extrapolate == TRUE) {
    completed_series <- na.omit(completed_series)
    yleft_comp <- completed_series[1, 2]
    yright_com <- completed_series[nrow(completed_series), 2]
    seq <- seq(
      from = min(my.data[, 1]),
      to = max(my.data[, 1]),
      by = my.data[, 1][2] - my.data[, 1][1]
    )
    app <- approx(
      x = completed_series[, 1],
      y = completed_series[, 2],
      xout = seq,
      method = "linear",
      yleft = yleft_comp,
      yright = yright_com
    )
    completed_series <- as.data.frame(cbind(app$x, app$y))
  }
  if (genplot == TRUE) {
    if (keep_editable == FALSE) {
      oldpar <- par(no.readonly = TRUE)
      on.exit(par(oldpar))
    }
    plot(completed_series,
         type = "l",
         col = "red")
    lines(tracked_curve, col = "black")
  }
  return(completed_series)
}
#this function is needed to do the numerical age-modelling 

curve2time_unc_anchor <- function (age_constraint = NULL, tracked_cycle_curve = NULL, 
  tracked_cycle_period = NULL, tracked_cycle_period_unc = NULL, 
  tracked_cycle_period_unc_dist = "n", n_simulations = 20, 
  gap_constraints = NULL, proxy_data = NULL, cycles_check = NULL, 
  uncer_cycles_check = NULL, max_runs = 1000, run_multicore = FALSE, 
  verbose = FALSE, genplot = FALSE, keep_nr = 2, keep_all_time_curves = FALSE, 
  dj = 1/200, lowerPeriod = 1, upperPeriod = 4600, omega_nr = 6, 
  seed_nr = 1337, dir = TRUE) 
{
dat <- as.matrix(tracked_cycle_curve[, ])
dat <- na.omit(dat)
dat <- dat[order(dat[, 1], na.last = NA, decreasing = F), ]
npts <- length(dat[, 1])
start <- dat[1, 1]
end <- dat[length(dat[, 1]), 1]
x1 <- dat[1:(npts - 1), 1]
x2 <- dat[2:(npts), 1]
dx = x2 - x1
dt = median(dx)
xout <- seq(start, end, by = dt)
npts <- length(xout)
interp <- approx(dat[, 1], dat[, 2], xout, method = "linear", n = npts)
interp_2 <- approx(dat[, 1], dat[, 3], xout, method = "linear", n = npts)
tracked_cycle_curve_2 <- cbind(interp[[1]], interp[[2]], interp_2[[2]])
out <- matrix(
  data = NA,
  nrow = nrow(tracked_cycle_curve_2),
  ncol = n_simulations
)
multi_tracked <- tracked_cycle_curve_2
age_curve <- tracked_cycle_curve_2[, c(1, 2)]
x_axis <- tracked_cycle_curve_2[, c(1)]
res_matrix <- matrix(data = NA,
                     nrow = nrow(age_curve),
                     ncol = n_simulations)
if (verbose == TRUE) {
  pb <- txtProgressBar(max = n_simulations, style = 3)
  progress <- function(n)
    setTxtProgressBar(pb, n)
  opts <- list(progress = progress)
}else {
  opts = NULL
}
set.seed(seed_nr)
if (nrow(age_constraint) > 0) {
  if (run_multicore == FALSE) {
    res_list <- list()
    for (i in 1:n_simulations) {
      check_astro_age <- matrix(
        data = NA,
        ncol = 1,
        nrow = nrow(age_constraint)
      )
      anchor_depth <- matrix(
        data = NA,
        ncol = 1,
        nrow = nrow(age_constraint)
      )
      anchor_age <- matrix(
        data = NA,
        ncol = 1,
        nrow = nrow(age_constraint)
      )
      if (keep_all_time_curves == TRUE) {
        time_curve_comb <- matrix(data = NA,
                                  ncol = 0,
                                  nrow = length(x_axis))
      }
      for (klm in 1:nrow(age_constraint)) {
        if (age_constraint[klm, 4] == "u") {
          check_astro_age[klm] <- runif(
            1,
            min = tracked_cycle_period -
              tracked_cycle_period_unc,
            max = tracked_cycle_period +
              tracked_cycle_period_unc
          )
        }
        if (age_constraint[klm, 4] == "n") {
          check_astro_age[klm] <- rnorm(1,
                                        mean = as.numeric(age_constraint[klm, 2]),
                                        sd = as.numeric(age_constraint[klm, 3]))
        }
      }
      anchor_astr <- 1
      anchor_radio <- 0
      sel_rws <- seq(from = 1,
                     to = nrow(age_constraint),
                     by = 1)
      runs <- 0
      anchor_diff <- matrix(data = NA,
                            ncol = 0,
                            nrow = length(sel_rws))
      while (anchor_astr > anchor_radio) {
        age_constraint_2 <- age_constraint[c(sel_rws), ]
        check_astro_age_2 <- check_astro_age[c(sel_rws), ]
        anchor_depth_2 <- anchor_depth[c(sel_rws), ]
        anchor_age_2 <- anchor_age[c(sel_rws), ]
        validator <- 1
        while (validator == 1) {
          new_curve <- multi_tracked[, c(1, 2)]
          val <- rnorm(1, mean = multi_tracked[1, 2], sd = multi_tracked[1, 3])
          pnorm_val <- 1 - pnorm(val,
                                 mean = multi_tracked[1, 2],
                                 sd = multi_tracked[1, 3],
                                 lower.tail = FALSE)
          for (j in 1:nrow(new_curve)) {
            new_curve[j, 2] <- 1 / (qnorm(pnorm_val, mean = multi_tracked[j, 2], sd = multi_tracked[j, 3]))
          }
          if (tracked_cycle_period_unc_dist == "u") {
            tracked_cycle_period_new <- runif(
              1,
              min = tracked_cycle_period -
                tracked_cycle_period_unc,
              max = tracked_cycle_period +
                tracked_cycle_period_unc
            )
          }
          if (tracked_cycle_period_unc_dist == "n") {
            tracked_cycle_period_new <- rnorm(1, mean = tracked_cycle_period, sd = tracked_cycle_period_unc)
          }
          time_curve <- WaverideR::curve2time(
            tracked_cycle_curve = new_curve,
            tracked_cycle_period = tracked_cycle_period_new,
            genplot = FALSE,
            keep_editable = FALSE
          )
          dif_mat <- time_curve[2:(nrow(time_curve)), 2] - time_curve[1:(nrow(time_curve) -
                                                                           1), 2]
          dif_mat_min <- min(dif_mat)
          if (dif_mat_min > 0) {
            validator <- 0
          }
        }
        if (dir == FALSE) {
          time_curve[, 2] <- max(time_curve[, 2]) -
            time_curve[, 2]
        }
        gaps_dur <- 0
        if (is.null(gap_constraints) == FALSE) {
          gaps_dur <- matrix(
            data = NA,
            nrow = nrow(gap_constraints[, ]),
            ncol = 1
          )
          for (qx in 1:nrow(gap_constraints)) {
            if (gap_constraints[qx, 4] == "u") {
              if (as.numeric(gap_constraints[qx, 1]) -
                  as.numeric(gap_constraints[qx, 2]) <
                  0) {
                a <- 0
              }
              else {
                a <- as.numeric(gap_constraints[qx, 1]) - as.numeric(gap_constraints[qx, 2])
              }
              gap_dur_new <- runif(
                1,
                min = a,
                max = as.numeric(gap_constraints[qx, 1]) + as.numeric(gap_constraints[qx, 2])
              )
            }
            if (gap_constraints[qx, 4] == "n") {
              gap_dur_new <- rnorm(
                1,
                mean = as.numeric(gap_constraints[qx, 1]),
                sd = as.numeric(gap_constraints[qx, 2])
              )
              if (gap_dur_new < 0) {
                gap_dur_new <- 0
              }
            }
            gaps_dur[qx, 1] <- gap_dur_new
            row_nr <- DescTools::Closest(time_curve[, 1],
                                         as.numeric(gap_constraints[qx, 3]),
                                         which = TRUE)
            out_3 <- time_curve[, 2]
            out_4 <- out_3[row_nr:length(out_3)]
            if (dir == FALSE) {
              out_4 <- out_4 - gap_dur_new
              time_curve[, 2] <- c(out_3[1:(row_nr -
                                              1)], (out_3[row_nr:length(out_3)] -
                                                      gap_dur_new))
            }
            else {
              out_4 <- (out_4 + gap_dur_new)
              time_curve[, 2] <- c(out_3[1:(row_nr -
                                              1)], (out_3[row_nr:length(out_3)] +
                                                      gap_dur_new))
            }
          }
        }
        if (keep_all_time_curves == TRUE) {
          time_curve_comb <- cbind(time_curve_comb, time_curve[, 2])
        }
        anchor_depth_2 <- matrix(data = NA,
                                 ncol = 1,
                                 nrow = length(sel_rws))
        anchor_age_2 <- matrix(data = NA,
                               ncol = 1,
                               nrow = length(sel_rws))
        anchor_astro_age <- matrix(data = NA,
                                   ncol = 1,
                                   nrow = length(sel_rws))
        for (klm in 1:nrow(age_constraint_2)) {
          if (age_constraint_2[klm, 7] == "u") {
            anchor_depth_new <- runif(
              1,
              min = as.numeric(age_constraint_2[klm, 5]) - as.numeric(age_constraint_2[klm, 6]) /
                2,
              max = as.numeric(age_constraint_2[klm, 5]) + as.numeric(age_constraint_2[klm, 6]) /
                2
            )
          }
          if (age_constraint_2[klm, 7] == "n") {
            anchor_depth_new <- rnorm(
              1,
              mean = as.numeric(age_constraint_2[klm, 4]),
              sd = as.numeric(age_constraint_2[klm, 4])
            )
          }
          if (age_constraint_2[klm, 7] == "t") {
            trap_par <- as.numeric(unlist(strsplit(age_constraint_2[klm, 5], " +")))
            anchor_depth_new <- trapezoid::rtrapezoid(
              1,
              min = trap_par[1],
              mode1 = trap_par[2],
              mode2 = trap_par[3],
              max = trap_par[3],
              n1 = 2,
              n3 = 2,
              alpha = 1
            )
          }
          if (age_constraint_2[klm, 4] == "u") {
            anchor_age_new <- runif(
              1,
              min = as.numeric(age_constraint_2[klm, 2]) - as.numeric(age_constraint_2[klm, 3]) /
                2,
              max = as.numeric(age_constraint_2[klm, 2]) + as.numeric(age_constraint_2[klm, 3]) /
                2
            )
          }
          if (age_constraint_2[klm, 4] == "n") {
            anchor_age_new <- rnorm(
              1,
              mean = as.numeric(age_constraint_2[klm, 2]),
              sd = as.numeric(age_constraint_2[klm, 3])
            )
          }
          anchor_depth_2[klm] <- anchor_depth_new
          anchor_age_2[klm] <- anchor_age_new
          row_nr <- DescTools::Closest(time_curve[, 1], anchor_depth_2[klm], which = TRUE)
          anchor_astro_age[klm] <- time_curve[row_nr, 2]
        }
        ages_radio <- anchor_age_2
        ages_astro <- anchor_astro_age
        ages_astro_anchored <- ages_astro
        ages_sim <- cbind(ages_radio, ages_astro)
        if(nrow(ages_sim)>=2){
        ages_sim <- ages_sim[order(ages_sim[, 1]), ]}
        check_astro_age_2 <- check_astro_age_2[order(check_astro_age_2)]
        time_curve_anchored <- time_curve
        
        
        if (ages_sim[1, 2] > ages_sim[nrow(ages_sim), 2]) {
          a <- mean(ages_radio) + mean(ages_astro)
          ages_astro_anchored <- a - ages_astro
          time_curve_anchored[, 2] <- a - time_curve[, 2]
        }
        else {
          a <- mean(ages_radio) - mean(ages_astro)
          ages_astro_anchored <- a + ages_astro
          time_curve_anchored[, 2] <- a + time_curve[, 2]
        }
        dif_radio <- abs(check_astro_age_2 - ages_radio)
        dif_astro <- abs(check_astro_age_2 - ages_astro_anchored)
        rown_nr <- order(dif_astro, decreasing = TRUE)[1]
        dif_astro_2 <- dif_astro
        dif_astro_2[, ] <- 0
        dif_astro_2[rown_nr] <- 1
        anchor_diff <- cbind(anchor_diff, dif_astro_2)
        if ((sum(sign(dif_astro - dif_radio)) * -1) ==
            nrow(age_constraint_2)) {
          anchor_astro_age <- cbind(age_constraint[c(sel_rws), 1], ages_astro_anchored)
          anchor_astr <- -1
        }
        else {
          runs <- runs + 1
        }
        if (runs == max_runs) {
          anchor_diff_rw_sum <- matrixStats::rowSums2(as.matrix(anchor_diff))
          row_nr <- DescTools::Closest(anchor_diff_rw_sum,
                                       max(anchor_diff_rw_sum),
                                       which = TRUE)
          if (length(sel_rws) <= keep_nr) {
            anchor_diff <- anchor_diff[c(sel_rws)]
            runs <- 0
          }
          else {
            sel_rws <- sel_rws[-c(row_nr)]
            anchor_diff <- anchor_diff[c(sel_rws)]
            runs <- 0
          }
        }
      }
      if (keep_all_time_curves == TRUE) {
        result <- list(time_curve_anchored[, 2],
                       anchor_astro_age,
                       gaps_dur,
                       time_curve_comb)
      }
      else
        (result <- list(time_curve_anchored[, 2], anchor_astro_age, gaps_dur))
      res_list <- list.append(res_list, result)
      if (verbose == TRUE) {
        setTxtProgressBar(pb, i)
      }
    }
  }
  if (run_multicore == TRUE) {
    numCores <- detectCores()
    cl <- parallel::makeCluster(numCores - 2)
    registerDoSNOW(cl)
    i <- 1
    fit <- foreach(i = 1:n_simulations, .options.snow = opts) %dopar%
      {
        
        
        check_astro_age <- matrix(
          data = NA,
          ncol = 1,
          nrow = nrow(age_constraint)
        )
        anchor_depth <- matrix(
          data = NA,
          ncol = 1,
          nrow = nrow(age_constraint)
        )
        anchor_age <- matrix(
          data = NA,
          ncol = 1,
          nrow = nrow(age_constraint)
        )
        if (keep_all_time_curves == TRUE) {
          time_curve_comb <- matrix(data = NA,
                                    ncol = 0,
                                    nrow = length(x_axis))
        }
        for (klm in 1:nrow(age_constraint)) {
          if (age_constraint[klm, 4] == "u") {
            check_astro_age[klm] <- runif(
              1,
              min = tracked_cycle_period -
                tracked_cycle_period_unc,
              max = tracked_cycle_period +
                tracked_cycle_period_unc
            )
          }
          if (age_constraint[klm, 4] == "n") {
            check_astro_age[klm] <- rnorm(1,
                                          mean = as.numeric(age_constraint[klm, 2]),
                                          sd = as.numeric(age_constraint[klm, 3]))
          }
        }
        anchor_astr <- 1
        anchor_radio <- 0
        sel_rws <- seq(from = 1,
                       to = nrow(age_constraint),
                       by = 1)
        runs <- 0
        anchor_diff <- matrix(data = NA,
                              ncol = 0,
                              nrow = length(sel_rws))
        while (anchor_astr > anchor_radio) {
          age_constraint_2 <- age_constraint[c(sel_rws), ]
          check_astro_age_2 <- check_astro_age[c(sel_rws), ]
          anchor_depth_2 <- anchor_depth[c(sel_rws), ]
          anchor_age_2 <- anchor_age[c(sel_rws), ]
          validator <- 1
          while (validator == 1) {
            new_curve <- multi_tracked[, c(1, 2)]
            val <- rnorm(1, mean = multi_tracked[1, 2], sd = multi_tracked[1, 3])
            pnorm_val <- 1 - pnorm(val,
                                   mean = multi_tracked[1, 2],
                                   sd = multi_tracked[1, 3],
                                   lower.tail = FALSE)
            for (j in 1:nrow(new_curve)) {
              new_curve[j, 2] <- 1 / (qnorm(pnorm_val, mean = multi_tracked[j, 2], sd = multi_tracked[j, 3]))
            }
            if (tracked_cycle_period_unc_dist == "u") {
              tracked_cycle_period_new <- runif(
                1,
                min = tracked_cycle_period - tracked_cycle_period_unc,
                max = tracked_cycle_period + tracked_cycle_period_unc
              )
            }
            if (tracked_cycle_period_unc_dist == "n") {
              tracked_cycle_period_new <- rnorm(1, mean = tracked_cycle_period, sd = tracked_cycle_period_unc)
            }
            time_curve <- WaverideR::curve2time(
              tracked_cycle_curve = new_curve,
              tracked_cycle_period = tracked_cycle_period_new,
              genplot = FALSE,
              keep_editable = FALSE
            )
            dif_mat <- time_curve[2:(nrow(time_curve)), 2] - time_curve[1:(nrow(time_curve) -
                                                                             1), 2]
            dif_mat_min <- min(dif_mat)
            if (dif_mat_min > 0) {
              validator <- 0
            }
          }
          if (dir == FALSE) {
            time_curve[, 2] <- max(time_curve[, 2]) -
              time_curve[, 2]
          }
          gaps_dur <- 0
          if (is.null(gap_constraints) == FALSE) {
            gaps_dur <- matrix(
              data = NA,
              nrow = nrow(gap_constraints[, ]),
              ncol = 1
            )
            for (qx in 1:nrow(gap_constraints)) {
              if (gap_constraints[qx, 4] == "u") {
                if (as.numeric(gap_constraints[qx, 1]) - as.numeric(gap_constraints[qx, 2]) < 0) {
                  a <- 0
                }
                else {
                  a <- as.numeric(gap_constraints[qx, 1]) - as.numeric(gap_constraints[qx, 2])
                }
                gap_dur_new <- runif(
                  1,
                  min = a,
                  max = as.numeric(gap_constraints[qx, 1]) + as.numeric(gap_constraints[qx, 2])
                )
              }
              if (gap_constraints[qx, 4] == "n") {
                gap_dur_new <- rnorm(
                  1,
                  mean = as.numeric(gap_constraints[qx, 1]),
                  sd = as.numeric(gap_constraints[qx, 2])
                )
                if (gap_dur_new < 0) {
                  gap_dur_new <- 0
                }
              }
              gaps_dur[qx, 1] <- gap_dur_new
              row_nr <- DescTools::Closest(time_curve[, 1],
                                           as.numeric(gap_constraints[qx, 3]),
                                           which = TRUE)
              out_3 <- time_curve[, 2]
              out_4 <- out_3[row_nr:length(out_3)]
              if (dir == FALSE) {
                out_4 <- out_4 - gap_dur_new
                time_curve[, 2] <- c(out_3[1:(row_nr -
                                                1)], (out_3[row_nr:length(out_3)] -
                                                        gap_dur_new))
              }
              else {
                out_4 <- (out_4 + gap_dur_new)
                time_curve[, 2] <- c(out_3[1:(row_nr -
                                                1)], (out_3[row_nr:length(out_3)] +
                                                        gap_dur_new))
              }
            }
          }
          if (keep_all_time_curves == TRUE) {
            time_curve_comb <- cbind(time_curve_comb, time_curve[, 2])
          }
          anchor_depth_2 <- matrix(data = NA,
                                   ncol = 1,
                                   nrow = length(sel_rws))
          anchor_age_2 <- matrix(data = NA,
                                 ncol = 1,
                                 nrow = length(sel_rws))
          anchor_astro_age <- matrix(data = NA,
                                     ncol = 1,
                                     nrow = length(sel_rws))
          for (klm in 1:nrow(age_constraint_2)) {
            if (age_constraint_2[klm, 7] == "u") {
              anchor_depth_new <- runif(
                1,
                min = as.numeric(age_constraint_2[klm, 5]) - as.numeric(age_constraint_2[klm, 6]) /
                  2,
                max = as.numeric(age_constraint_2[klm, 5]) + as.numeric(age_constraint_2[klm, 6]) /
                  2
              )
            }
            if (age_constraint_2[klm, 7] == "n") {
              anchor_depth_new <- rnorm(
                1,
                mean = as.numeric(age_constraint_2[klm, 4]),
                sd = as.numeric(age_constraint_2[klm, 4])
              )
            }
            if (age_constraint_2[klm, 7] == "t") {
              trap_par <- as.numeric(unlist(strsplit(age_constraint_2[klm, 5], " +")))
              anchor_depth_new <- trapezoid::rtrapezoid(
                1,
                min = trap_par[1],
                mode1 = trap_par[2],
                mode2 = trap_par[3],
                max = trap_par[3],
                n1 = 2,
                n3 = 2,
                alpha = 1
              )
            }
            if (age_constraint_2[klm, 4] == "u") {
              anchor_age_new <- runif(
                1,
                min = as.numeric(age_constraint_2[klm, 2]) - as.numeric(age_constraint_2[klm, 3]) /
                  2,
                max = as.numeric(age_constraint_2[klm, 2]) + as.numeric(age_constraint_2[klm, 3]) /
                  2
              )
            }
            if (age_constraint_2[klm, 4] == "n") {
              anchor_age_new <- rnorm(
                1,
                mean = as.numeric(age_constraint_2[klm, 2]),
                sd = as.numeric(age_constraint_2[klm, 3])
              )
            }
            anchor_depth_2[klm] <- anchor_depth_new
            anchor_age_2[klm] <- anchor_age_new
            row_nr <- DescTools::Closest(time_curve[, 1], anchor_depth_2[klm], which = TRUE)
            anchor_astro_age[klm] <- time_curve[row_nr, 2]
          }
          ages_radio <- anchor_age_2
          ages_astro <- anchor_astro_age
          ages_astro_anchored <- ages_astro
          ages_sim <- cbind(ages_radio, ages_astro)
        if(nrow(ages_sim)>=2){
        ages_sim <- ages_sim[order(ages_sim[, 1]), ]}
          check_astro_age_2 <- check_astro_age_2[order(check_astro_age_2)]
          time_curve_anchored <- time_curve

          if (ages_sim[1, 2] > ages_sim[nrow(ages_sim), 2]) {
            a <- mean(ages_radio) + mean(ages_astro)
            ages_astro_anchored <- a - ages_astro
            time_curve_anchored[, 2] <- a - time_curve[, 2]
          }
          else {
            a <- mean(ages_radio) - mean(ages_astro)
            ages_astro_anchored <- a + ages_astro
            time_curve_anchored[, 2] <- a + time_curve[, 2]
          }
          dif_radio <- abs(check_astro_age_2 - ages_radio)
          dif_astro <- abs(check_astro_age_2 - ages_astro_anchored)
          rown_nr <- order(dif_astro, decreasing = TRUE)[1]
          dif_astro_2 <- dif_astro
          dif_astro_2[, ] <- 0
          dif_astro_2[rown_nr] <- 1
          anchor_diff <- cbind(anchor_diff, dif_astro_2)
          sel_proxy_nr <- round(runif(
            n = 1,
            min = 1,
            max = length(proxy_data)
          ), 0)
          my.data <- proxy_data[[sel_proxy_nr]]
          out <- time_curve_anchored
          completed_series <- na.omit(out)
          yleft_comp <- completed_series[1, 2]
          yright_com <- completed_series[nrow(completed_series), 2]
          app <- approx(
            x = out[, 1],
            y = out[, 2],
            xout = my.data[, 1],
            method = "linear",
            yleft = yleft_comp,
            yright = yright_com
          )
          completed_series <- as.data.frame(cbind(app$y, my.data[, 2]))
          dat <- as.matrix(completed_series)
          dat <- na.omit(dat)
          dat <- dat[order(dat[, 1], na.last = NA, decreasing = F), ]
          npts <- length(dat[, 1])
          start <- dat[1, 1]
          end <- dat[length(dat[, 1]), 1]
          x1 <- dat[1:(npts - 1), 1]
          x2 <- dat[2:(npts), 1]
          dx = x2 - x1
          dt = median(dx)
          xout <- seq(start, end, by = dt)
          npts <- length(xout)
          interp <- approx(dat[, 1], dat[, 2], xout, method = "linear", n = npts)
          completed_series <- as.data.frame(interp)
          wt_res <- WaverideR::analyze_wavelet(
            data = completed_series,
            dj = dj,
            lowerPeriod = lowerPeriod,
            upperPeriod = upperPeriod,
            verbose = FALSE,
            omega_nr = omega_nr
          )
          avg_wt <- cbind(wt_res$Period, wt_res$Power.avg)
          avg_wt <- WaverideR::max_detect(data = avg_wt, pts = 5)
          mtm_res_test <- is.na(avg_wt)
          mtm_res_test <- mtm_res_test[1]
          if (mtm_res_test == TRUE) {
            runs <- runs + 1
          }
          else if ((sum(sign(dif_astro - dif_radio)) *
                    -1) == nrow(age_constraint_2) |
                   nrow(age_constraint_2) ==
                   1) {
            mtm_per <- avg_wt[, 1]
            high_vals <- cycles_check + uncer_cycles_check
            low_vals <- cycles_check - uncer_cycles_check
            check <- matrix(
              data = NA,
              nrow = length(cycles_check),
              ncol = 1
            )
            for (i in 1:length(cycles_check)) {
              check[i, 1] <- any(mtm_per < high_vals[i] &
                                   mtm_per > low_vals[i])
            }
            if (sum(check) == length(cycles_check)) {
              anchor_astro_age <- cbind(age_constraint[c(sel_rws), 1], ages_astro_anchored)
              anchor_astr <- -1
            }
            else {
              runs <- runs + 1
              print(runs)
            }
          }
          else {
            runs <- runs + 1
            print(runs)
          }
          if (runs == max_runs) {
            anchor_diff_rw_sum <- matrixStats::rowSums2(as.matrix(anchor_diff))
            row_nr <- DescTools::Closest(anchor_diff_rw_sum,
                                         max(anchor_diff_rw_sum),
                                         which = TRUE)
            if (length(sel_rws) <= keep_nr) {
              anchor_diff <- anchor_diff[c(sel_rws)]
              runs <- 0
            }
            else {
              sel_rws <- sel_rws[-c(row_nr)]
              anchor_diff <- anchor_diff[c(sel_rws)]
              runs <- 0
            }
          }
        }
        time_curve_anchored <- time_curve_anchored[, 2]
        if (keep_all_time_curves == TRUE) {
          result <- list(time_curve_anchored,
                         anchor_astro_age,
                         gaps_dur,
                         time_curve_comb)
        }
        else {
          (result <- list(time_curve_anchored, anchor_astro_age, gaps_dur))
        }
      }
    res_list <- fit
    stopCluster(cl)
  }
}
result_matrix <- matrix(data = NA,
                        nrow = nrow(age_curve),
                        ncol = 0)
res_radio <- matrix(data = NA, nrow = 0, ncol = 2)
colnames(res_radio) <- c("name", "age")
res_time_runs <- matrix(data = NA,
                        nrow = length(x_axis),
                        ncol = 0)
if (is.null(gap_constraints) == FALSE & keep_all_time_curves ==
    FALSE) {
  res_gap <- matrix(data = NA,
                    nrow = 0,
                    ncol = nrow(gap_constraints))
  colnames(res_gap) <- c(gap_constraints[, 3])
  for (i in 1:length(res_list)) {
    time_curve <- res_list[[i]][[1]]
    new_anchor_dates <- res_list[[i]][[2]]
    new_gap_dur <- res_list[[i]][[3]]
    result_matrix <- cbind(result_matrix, time_curve)
    colnames(new_anchor_dates) <- c("name", "age")
    res_radio <- rbind(res_radio, new_anchor_dates)
    new_gap_dur <- t(new_gap_dur)
    colnames(new_gap_dur) <- c(gap_constraints[, 3])
    res_gap <- rbind(as.matrix(res_gap), new_gap_dur)
    res_radio_split <- split(x = res_radio[, 2], f = as.factor(unique(res_radio[, 1])))
    for (i in 1:length(res_radio_split)) {
      res_radio_split[[i]] <- as.numeric(res_radio_split[[i]])
    }
  }
  result <- list(x_axis, result_matrix, res_gap, res_radio_split)
}
if (is.null(gap_constraints) == TRUE & keep_all_time_curves ==
    FALSE) {
  for (i in 1:length(res_list)) {
    time_curve <- res_list[[i]][[1]]
    new_anchor_dates <- res_list[[i]][[2]]
    result_matrix <- cbind(result_matrix, time_curve)
    colnames(new_anchor_dates) <- c("name", "age")
    res_radio <- rbind(res_radio, new_anchor_dates)
    res_radio_split <- split(x = res_radio[, 2], f = as.factor(unique(res_radio[, 1])))
    for (i in 1:length(res_radio_split)) {
      res_radio_split[[i]] <- as.numeric(res_radio_split[[i]])
    }
  }
  result <- list(x_axis, result_matrix, res_radio_split)
}
if (is.null(gap_constraints) == FALSE & keep_all_time_curves ==
    TRUE) {
  res_gap <- matrix(data = NA,
                    nrow = 0,
                    ncol = nrow(gap_constraints))
  colnames(res_gap) <- c(gap_constraints[, 3])
  for (i in 1:length(res_list)) {
    time_curve <- res_list[[i]][[1]]
    new_anchor_dates <- res_list[[i]][[2]]
    new_gap_dur <- res_list[[i]][[3]]
    time_curve_all <- res_list[[i]][[4]]
    result_matrix <- cbind(result_matrix, time_curve)
    colnames(new_anchor_dates) <- c("name", "age")
    res_radio <- rbind(res_radio, new_anchor_dates)
    new_gap_dur <- t(new_gap_dur)
    colnames(new_gap_dur) <- c(gap_constraints[, 3])
    res_gap <- rbind(as.matrix(res_gap), new_gap_dur)
    res_time_runs <- cbind(res_time_runs, time_curve_all)
    res_radio_split <- split(x = res_radio[, 2], f = as.factor(unique(res_radio[, 1])))
    for (i in 1:length(res_radio_split)) {
      res_radio_split[[i]] <- as.numeric(res_radio_split[[i]])
    }
  }
  result <- list(x_axis,
                 result_matrix,
                 res_gap,
                 res_radio_split,
                 res_time_runs)
}
if (is.null(gap_constraints) == TRUE & keep_all_time_curves ==
    TRUE) {
  for (i in 1:length(res_list)) {
    time_curve <- res_list[[i]][[1]]
    new_anchor_dates <- res_list[[i]][[2]]
    time_curve_all <- res_list[[i]][[4]]
    result_matrix <- cbind(result_matrix, time_curve)
    colnames(new_anchor_dates) <- c("name", "age")
    res_radio <- rbind(res_radio, new_anchor_dates)
    res_time_runs <- cbind(res_time_runs, time_curve_all)
    res_radio_split <- split(x = res_radio[, 2], f = as.factor(unique(res_radio[, 1])))
    for (i in 1:length(res_radio_split)) {
      res_radio_split[[i]] <- as.numeric(res_radio_split[[i]])
    }
  }
  result <- list(x_axis, result_matrix, res_radio_split, res_time_runs)
}
if (genplot == TRUE) {
  plot(
    age_constraint[, 5],
    age_constraint[, 2],
    col = "black",
    cex = 2,
    type = "p",
    ylim = c(min(result[[2]]), max(result[[2]])),
    xlim = c(max(x_axis), min(x_axis))
  )
  points(
    age_constraint[, 5],
    as.numeric(age_constraint[, 2]) + 2 * as.numeric(age_constraint[, 3]),
    col = "red",
    cex = 1,
    pch = 6
  )
  points(
    age_constraint[, 5],
    as.numeric(age_constraint[, 2]) - 2 * as.numeric(age_constraint[, 3]),
    col = "blue",
    cex = 1,
    pch = 2
  )
  res <- result[[2]]
  sds <- rowSds(result[[2]])
  lines(x_axis, rowMeans(result[[2]]), col = "black")
  lines(x_axis, rowMeans(result[[2]]) + 2 * sds, col = "red")
  lines(x_axis, rowMeans(result[[2]]) - 2 * sds, col = "blue")
}
  return(result)
}

induration <- read.csv(
  "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/induration.csv"
)

induration <- sortNave(induration[,c(2,1)],genplot=FALSE)
induration <- linterp(induration,0.01,genplot=FALSE)

induration[,1] <- seq(25,-7.20,length.out=length(induration[,1]))
induration <- linterp(induration,0.01,genplot=FALSE)
induration <- mwStats(induration,win=0.05,ends=TRUE,genplot=FALSE)
induration <- linterp(induration,0.01,genplot=FALSE)

induration[,2] <- induration[,2]/max(induration[,2])
induration[,2] <- sqrt(induration[,2])
induration[,2] <- induration[,2]-min(induration[,2])
induration[,2] <- induration[,2]/max(induration[,2])
induration[,2] <- induration[,2]*5


kosov_isotopes <- read.csv(
  "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/kosov_isotopes.csv",header =TRUE
)

kosov_d13Ccarb <- kosov_isotopes[,c(3,6)]
kosov_d13Ccarb <- na.omit(kosov_d13Ccarb)
kosov_d13Ccarb <- sortNave(kosov_d13Ccarb,genplot=FALSE)

kosov_d18O <- kosov_isotopes[,c(3,4)]
kosov_d18O <- na.omit(kosov_d18O)
kosov_d18O <- sortNave(kosov_d18O,genplot=FALSE)

kosov_d13Corg<- kosov_isotopes[,c(3,5)]
kosov_d13Corg <- na.omit(kosov_d13Corg)
kosov_d13Corg <- sortNave(kosov_d13Corg,genplot=FALSE)


kosov_big13C <- kosov_isotopes[,c(3,5,6)]
kosov_big13C <- na.omit(kosov_big13C)
kosov_big13C[,2] <- kosov_big13C[,3]-kosov_big13C[,2]

# URL of the raw image
url <- "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/kosov_log.jpg"

# Download into memory
res <- GET(url)
stop_for_status(res)

# Read the JPEG from the raw content
img <- readJPEG(content(res, "raw"))



# URL of the raw image
url_2 <- "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/regional_overview.jpg"

# Download into memory
url_2_get <- GET(url_2)
stop_for_status(url_2_get)

# Read the JPEG from the raw content
img_2 <- readJPEG(content(url_2_get, "raw"))

my_data <- data.frame(
  Interval = c("Entire record",
               "Lau biogeochemical Event", 
               "LKB Event (trace metal peak)",
               "R-zone", 
               "S-Zone", 
               "F-Zone",
               "Ludlow part section",
               "Pridoli part section"),
  position_bottom =c(-7.20,
               -0.4, 
               -0.4,
               0, 
               1.4, 
               11.6,
               0,
               22),
  position_top=c(25,
               20, 
               0,
               1.4, 
               11.6, 
               20,
               22,
               25))
my_data$age_bottom <- NA
my_data$uncertainty_age_bottom <- NA
my_data$age_top <- NA
my_data$uncertainty_age_top <- NA
my_data$duration <- NA
my_data$uncertainty <- NA

Abstract

The Kosov Quarry section in the Prague Basin preserves one of the most complete Silurian successions spanning the Lau biogeochemical Event. This interval records major perturbations in the carbon cycle, climate cooling, redox fluctuations, and faunal turnover, yet the pacing and duration of these changes remain poorly constrained. Leveraging its exceptional completeness, we developed a new astrochronology based on the induration record, which allows the identification of cycles ranging from the half-precession to the 405-kyr eccentricity cycle. The astrochronology indicates that the Lau biogeochemical Event started at 424.06 ± 0.55 Ma and lasted 0.87 ± 0.03 Myr, while the associated Lau-Kozlowski Bio-event was much shorter at 30 ± 10 kyr. Astronomical forcing is expressed not only in the induration record but also in the rate of change (‰/kyr) of the δ13Ccarb curve and in the lag-1 sea-level proxy. The δ13Ccarb rate-of-change record shows a strong 100-kyr eccentricity imprint during the onset of the Lau Event, reaching a maximum of 0.09 ‰/kyr. Similarly, the lag-1 sea-level record is dominated by the 405-kyr eccentricity cycle and appears in-phase with insolation, consistent with (405-kyr) eccentricity-paced glacio-eustatic sea-level fluctuations during the Ludlow.

1. Introduction

The Lau biogeochemical Event is associated with one of the largest carbon cycle perturbations of the entire Phanerozoic. The Lau biogeochemical Event is marked by a large positive δ¹³C excursion, faunal turnover, and evidence for sea-level and redox fluctuations (Frýda and Manda, 2013; Frýda et al., 2020). These changes indicate a tight coupling between the carbon cycle, climate, and oceanographic processes; however, the tempo, pacing, and internal structure of the Event remain poorly constrained. Without a precise temporal framework, it is not possible to determine whether the Lau reflects a brief perturbation or a protracted interval of instability.

To address this, we will conduct a cyclostratigraphic study on the Kosov quarry section, which provides a nearly complete archive, allowing us to establish a high-resolution astrochronological framework for the Lau biogeochemical Event. The induration record will form the basis for constructing the astrochronology, which will enable us to assign durations and associated uncertainties to the Lau biogeochemical Event and its associated (sub)intervals. The lag-1 sea-level proxy will be derived from the tuned induration record, which will provide constraints and insights into sea-level variability during the Lau biogeochemical event. The rate of change (‰/kyr) calculated from the δ13Ccarb record will be used to gain insights into carbon cycle dynamics during the Lau biogeochemical Event. Furthermore, the imprint of astronomical cycles on the δ13Ccarb rate of change (‰/kyr) and lag-1 sea-level will be delineated to understand the role that astronomical cycles might have played in pacing climate, organic carbon burial, and sea-level variations during the Lau biogeochemical Event.

1.1 Geological setting

Our study focuses on upper Silurian (Ludfordian) strata of the Prague Basin, a sedimentary succession within the Barrandian (Teplá-Barrandian terrane) (Frýda and Manda, 2013; Frýda et al., 2020, 2021) (Figure 1). During the Ludfordian, this block was located at ~25–30°S (Tasáryová et al., 2014; Scotese, 2021). Facies distribution indicates hemipelagic deposition surrounded by shallow- to deeper-marine environments (HORNÝ, 1955; Kříž, 1991). It remains debated whether the Barrandian’s geodynamic setting belongs to the peri-Gondwanan terrains or is its own isolated Perunica microcontinent (Fatka and Mergl, 2009).

Code
par(mar = c(4, 2, 2, 2))

# Empty plot to define margins and space in the panel
plot(1, type = "n", axes = FALSE, xlab = "", ylab = "", main="A")

# Get the coordinates of the current plot region
usr <- par("usr")  # c(x1, x2, y1, y2)

# Draw the image to fill the entire panel
rasterImage(img_2, usr[1], usr[3], usr[2], usr[4])

Figure 1. Regional overview of the Kosov section. Copied from Fryda et al. (2020)
Code
par(mar = c(4, 6, 2, 2))

The studied Kosov section (No.JF195; 49°56′12.2″N, 14°03′20.1″E) provides one of the most complete global records of the mid-Ludfordian carbon isotope excursion (Frýda and Manda, 2013; Frýda et al., 2020, 2021) (Figure 2).The succession comprises carbonates with marly interbeds and shales, including micritic mudstones, skeletal limestones, and crinoidal grainstones (Kříž, 1992; Lehnert et al., 2007). The site has been widely studied for its sedimentology, palaeontology, and faunal changes across the Lau/Kozlowskii Bioevent (LKB) and Mid-Ludfordian Carbon Isotope Excursion (MLCIE) (Kříž, 1992; Štorch, 1995; Manda and Kříž, 2006; Frýda and Manda, 2013).

Code
# Those data are embedded into WaverideR
#plot the data

Ludlow_col <- geo_col(name = "Ludlow")
Pridoli_col <- geo_col(name = "Pridoli")

layout.matrix <- matrix(c(1, 2,3,4,5), nrow = 1, ncol = 5)
graphics::layout(mat = layout.matrix,
                 heights = c(1,1,1,1), # Heights of the two rows
                 widths = c(1.5,0.75,0.5,1.5,1,1)) # Widths of the two columns

par(mar = c(4, 4, 2, 2))

# Empty plot to define margins and space in the panel
plot(1, type = "n", axes = FALSE, xlab = "", ylab = "", main="A")

# Get the coordinates of the current plot region
usr <- par("usr")  # c(x1, x2, y1, y2)

# Draw the image to fill the entire panel
rasterImage(img, usr[1], usr[3], usr[2], usr[4])
par(new=TRUE)

a <- round(c(max(induration[, 1]), min(induration[, 1])),0)
ylims <- c(min(induration[, 1]),max(induration[,1]))

plot(
  x = c(0, 1),
  y = c(min(induration[, 1]), max(induration[, 1])),
  col = "white",
  xlab = "",
  ylab = "Position (m)",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt = "n",
  ylim = ylims,axes=FALSE,
  main=""
)    


axis(2, at = (seq(a[1],
                          a[2], by = -1)),
     labels = (seq(a[1],
                      a[2], by = -1)))

par(mar = c(4, 4, 2, 0))

plot(
  x = c(0, 1),
  y = c(min(induration[, 1]), max(induration[, 1])),
  col = "white",
  xlab = "",
  ylab = "Postion (m)",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt= "n",
  ylim = ylims,
  main="B"
)    

axis(2, at = (seq(a[1],
                          a[2], by = -1)),
     labels = (seq(a[1],
                      a[2], by = -1)))

polygon(
    y = c(
      my_data[1,2],
      my_data[1,2],
      my_data[8,2],
      my_data[8,2]
    ),
    x = c(0, 1, 1, 0),
    col = Ludlow_col
  )

    polygon(
    y = c(
      my_data[8,2],
      my_data[8,2],
      my_data[8,3],
      my_data[8,3]
    ),
    x = c(0, 1, 1, 0),
    col = Pridoli_col
  )

    text(
    labels = "Ludlow",
    x = 0.5,
    srt = 270,cex=2,
    y = (my_data[1,2] +my_data[8,2]) / 2
  )

    text(
    labels = "Pridoli",
    x = 0.5,
    srt = 270,cex=2,
    y = (my_data[8,2] + my_data[8,3]) / 2
  )
 
   
par(mar = c(4, 0.5, 2, 0.5))


plot(
  x = c(0, 1),
  y = c(min(induration[, 1]), max(induration[, 1])),
  col = "white",
  xlab = "",
  ylab = "",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt = "n",
  ylim = ylims,
  main="C"
)    

polygon(
    y = c(
      my_data[4,2],
      my_data[4,2],
      my_data[5,2],
      my_data[5,2]
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
      labels = "R-zone",cex=1,
    x = 0.5,
    srt = 270,
    y = (my_data[4,2] + my_data[5,2]) / 2
  )

polygon(
    y = c(
      my_data[5,2],
      my_data[5,2],
      my_data[6,2],
      my_data[6,2]
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "S-zone",cex=2,
    x = 0.5,
    srt = 270,
    y = (my_data[6,2] + my_data[5,2]) / 2
  )

polygon(
    y = c(
      my_data[6,2],
      my_data[6,2],
      my_data[6,3],
      my_data[6,3]
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "F-zone",cex=2,
    x = 0.5,
    srt = 270,
    y = (my_data[6,2] + my_data[6,3]) / 2
  )

plot(induration[,2],induration[,1],type="l",yaxs="i",frame.plot=FALSE,ylab="Position (m)",xlab="induration",ylim=range(induration[,1]), main="D",yaxt="n")
par(mar = c(4, 1, 2, 2))

plot(kosov_d13Ccarb[,2],kosov_d13Ccarb[,1],type="l",yaxs="i",frame.plot=FALSE,ylab="Position (m)",xlab="δ13Ccarb",yaxt="n", main="E",col="green",ylim=range(induration[,1]))
points(kosov_d13Ccarb[,2],kosov_d13Ccarb[,1],col="darkgreen",pch=19)

polygon(
  x =
    c(
      min(kosov_d13Ccarb[, 2]) - 0.15,
      max(kosov_d13Ccarb[, 2]) * 1.1,
      max(kosov_d13Ccarb[, 2]) * 1.1,
      min(kosov_d13Ccarb[, 2]) - 0.15
    ),
  y = c(
    my_data[6,3],
    my_data[6,3],
    my_data[4,2],
    my_data[4,2]
  ),
  col = rgb(0, 0, 0, 0.25)
)

 text(
    labels = "Lau  δ13Ccarb excursion",
    x = 2,
    srt = 270,
    y = (my_data[4,2] + my_data[6,3]) / 2
  )
 
 
abline(h=my_data[3,2],col="red",lty=3,lwd=3) 

    text(
    labels = "Onset \n LKB",
    x =6,cex=1.25,
    srt = 0,
    y = (my_data[3,2])
  ) 

Figure 2. The original litholog, chronostratigraphy, event zonation, induration record and the δ13Ccarb curve. A. The Litholog of Frýda et al. (2020). B. Chronostratigraphic subdivision. C. Event stratigraphic intervals. D. The induration record extracted from the litholog E. The δ13Ccarb record of Frýda et al. (2020)

2. Materials and Methods

A cyclostratigraphic analysis will be conducted on the induration record (see SI for the R code). The analyses will leverage the function of the WaverideR (Arts, 2023; Arts et al., 2024) astrochron R packages (Meyers, 2015, 2019) to conduct the cyclostratigraphic study.

The cyclostratigraphic analysis begins by applying the Time Optimisation (TimeOpt) function of the astrochron R package to the induration record, yielding a first-order estimate of the sedimentation rate (Meyers, 2015). The TimeOpt function will be run twice, once to evaluate the 405-kyr eccentricity amplitude modulation of the 100-kyr eccentricity band, and the concentration of power at the 100-kyr and 405-kyr eccentricity frequencies, and once to evaluate the eccentricity amplitude modulation of the precession band, and the concentration of power at the precession and eccentricity frequencies. The optimal fit of the astronomical cycles will be evaluated for a range of sediment accumulation rates spanning between 0.1 and 4 cm/kyr.

The first-order sedimentation rate estimate from the TimeOpt runs is used to extract the 405-kyr eccentricity cycle from the induration record. The extracted cycle is then used to conduct a minimal tuning, where the distance between two successive peaks of the extracted cycle is set to the duration of the interpreted astronomical cycle. This duration is subsequently converted into an age model and a sedimentation-rate curve. The sedimentation-rate curve is then used to convert cm/kyr to the period (m) of different astronomical cycles. These cycles will then be plotted on top of the CWT scalogram to verify whether the curves pass through areas of high spectral power, validating the presence of astronomical cycles. If this is the case, the period (m) of the 405-kyr eccentricity cycle will be tracked in the CWT.

The tracked period (m) curve of the 405-kyr eccentricity cycle can then be used to create a floating astrochronology with an assigned uncertainty based on the analytical uncertainty of the Wavelet (Arts et al., 2024). This floating age model with uncertainty is then anchored to the astrochronologically calibrated age of the Ludlow-Pridoli boundary (423.03 ±0.53 Ma) (Arts et al., 2024). The anchored numerical age model can then be used to assign ages and durations (with uncertainty) to the record of the Kosov quarry section and its subsections.

The mean age model is then used to convert the induration and δ13Ccarb record to the time domain. Allowing for the study of the imprint of astronomical cycles in the induration record in the time domain. Special attention will be paid to the phase relationship between the 100-kyr eccentricity cycle directly extracted from the record and the 100-kyr eccentricity cycle extracted from the Hilbert transform of the precession cycle, and the phase relationship between the 405-kyr eccentricity cycle directly extracted from the record and the 405-kyr eccentricity cycle extracted from the Hilbert transform of the 100-kyr eccentricity cycle. The phase relationship between these cycles will allow us to infer the phase relationship between cycles extracted from the proxy record and those the true phase of the astronomical cycle, as well as its relationship with changes in insolation (Hinnov, 2000; Laskar et al., 2004).

The rate of change (‰/kyr) will be calculated from the δ13Ccarb record, which has been converted to the time domain. Which again will be investigated for the imprint of astronomical cycles. A lag-1 curve was generated based on the lag-1 autocorrelation coefficient using a windowed analysis Monte-Carlo on the induration record in the time domain. This record will function as a proxy record for the sea-level curve (Li et al., 2018). The imprint of astronomical cycles in the lag-1 record will be investigated and compared to the imprint of astronomical cycles in the induration recordd.

3. Results

3.1. TimeOpt-derived sedimentation rates

The TimeOpt function was run twice, once to optimise the 405-kyr eccentricity amplitude modulation of the 100-kyr eccentricity band, and the concentration of power at the 100-kyr and 405-kyr eccentricity frequencies and once to optimise the eccentricity amplitude modulation of the precession band, and the concentration of power at the precession and eccentricity frequencies. The TimeOpt results indicate that the optimal sedimentation rate is around 3 cm/ka, with the peak spanning sedimentation rates between 2 and 4 cm/ka (see Figure 3).

Code
#run timeOpt
#we need to run it twice for a correct plot later on
targetP_AM=c(405.7, 130.7, 123.8, 98.9,94.9)
targetP=c(21,17)

res1=timeOpt(dat = induration,
    sedmin = 0.1,
    sedmax = 4,
    numsed = 500,
    fit = 2,
    fitModPwr = TRUE,
    targetE=targetP_AM,
    roll = 10^4,output=1)

res2 <- timeOpt(dat = induration,
    sedmin = 0.1,
    sedmax = 4,
    numsed = 500,
    fit = 2,
    fitModPwr = TRUE,
    targetE=targetP_AM,
    roll = 10^4,output=2,genplot=TRUE)


res3=timeOpt(dat = induration,
    sedmin = 0.1,
    sedmax = 4,
    numsed = 500,
    fit = 1,
    fitModPwr = TRUE,
    targetP=targetP,
    roll = 10^4,output=1)

res4 <- timeOpt(dat = induration,
    sedmin = 0.1,
    sedmax = 4,
    numsed = 500,
    fit = 1,
    fitModPwr = TRUE,
    targetP=targetP,
    roll = 10^4,output=2,genplot=TRUE)
Code
layout(matrix(c(1, 2, 3, 4,5,6,7,8), nrow = 4, ncol = 2, byrow = TRUE))  
par(mar = c(4, 4, 3, 3))

# --- Panel A ---
plot(res1[, 1], res1[, 2], cex = 0.75, cex.lab = 1.2, 
     cex.main = 1.3, col = "red", xlab = "", ylab = "", 
     main = expression(paste(bold("Fit: "), {
       "r"^2
     }["envelope"], " (red) and ", {
       "r"^2
     }["power"], " (gray)")))
axis(2, col.axis = "red")
mtext(expression("r"^2), side = 2, line = 2, cex = 0.9)
mtext("Sedimentation rate (cm/ka)", side = 1, line = 2.3, cex = 0.8)
par(new = TRUE)
plot(res1[, 1], res1[, 3], col = "#00000064", 
     xlab = "", ylab = "", type = "l", axes = FALSE, lwd = 2)
axis(4, ylim = c(0, max(res1[, 3])), lwd = 1, col = "black")
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "A", pos = 4, font = 2, cex = 1.5)

# --- Panel B ---
plot(res1[, 1], res1[, 4], type = "l", lwd = 2, 
     cex.lab = 1.2, cex.main = 1.3, col = "black", 
     xlab = "", ylab = "", main = expression(paste(bold("Optimal Fit: "), {
       "r"^2
     }["opt"])))
mtext(expression({
  "r"^2
}["opt"]), side = 2, line = 1.9, cex = 0.9)
mtext("Sedimentation rate (cm/ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "B", pos = 4, font = 2, cex = 1.5)

# --- Panel C ---
ylim1 = c(min(res2[,4], res2[,5]), max(res2[,4], res2[,5]))
plot(res2[,1], res2[,4], cex = 0.5, cex.lab = 1.2, cex.main = 1.2, 
     xlab = "", ylab = "", main = "Envelope (red); Reconstructed Ecc. Model (black)", 
     col = "red", type = "l", ylim = ylim1)
lines(res2[, 1], res2[,5], lwd = 1.5)
mtext("Std. Value", side = 2, line = 2, cex = 0.9)
mtext("Time (ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "C", pos = 4, font = 2, cex = 1.5)

# --- Panel D ---
ylim1 = c(min(res2[, 3], -1 * res2[, 4]), max(res2[, 3], res2[, 4]))
plot(res2[,1],res2[,3], col = "blue", cex = 0.5, cex.lab = 1.2, 
     cex.main = 1.3, xlab = "", ylab = "", 
     main = "Envelope (red); Filtered Data (blue)", ylim = ylim1)
lines(res2[,1],res2[,3], col = "blue")
lines(res2[,1],res2[,4], col = "red")
lines(res2[,1], -1 *res2[,4], col = "red")
abline(h = 0, col = "black", lty = 3)
mtext("Std. Value", side = 2, line = 2, cex = 0.9)
mtext("Time (ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "D", pos = 4, font = 2, cex = 1.5)



# --- Panel E ---
plot( res3[, 1],  res3[, 2], cex = 0.75, cex.lab = 1.2, 
     cex.main = 1.3, col = "red", xlab = "", ylab = "", 
     main = expression(paste(bold("Fit: "), {
       "r"^2
     }["envelope"], " (red) and ", {
       "r"^2
     }["power"], " (gray)")))
axis(2, col.axis = "red")
mtext(expression("r"^2), side = 2, line = 2, cex = 0.9)
mtext("Sedimentation rate (cm/ka)", side = 1, line = 2.3, cex = 0.8)
par(new = TRUE)
plot( res3[, 1],  res3[, 3], col = "#00000064", 
     xlab = "", ylab = "", type = "l", axes = FALSE, lwd = 2)
axis(4, ylim = c(0, max( res3[, 3])), lwd = 1, col = "black")
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "E", pos = 4, font = 2, cex = 1.5)

# --- Panel F ---
plot( res3[, 1],  res3[, 4], type = "l", lwd = 2, 
     cex.lab = 1.2, cex.main = 1.3, col = "black", 
     xlab = "", ylab = "", main = expression(paste(bold("Optimal Fit: "), {
       "r"^2
     }["opt"])))
mtext(expression({
  "r"^2
}["opt"]), side = 2, line = 1.9, cex = 0.9)
mtext("Sedimentation rate (cm/ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "F", pos = 4, font = 2, cex = 1.5)

# --- Panel G ---
ylim1 = c(min( res4[,4],  res4[,5]), max( res4[,4],  res4[,5]))
plot( res4[,1],  res4[,4], cex = 0.5, cex.lab = 1.2, cex.main = 1.2, 
     xlab = "", ylab = "", main = "Envelope (red); Reconstructed Ecc. Model (black)", 
     col = "red", type = "l", ylim = ylim1)
lines( res4[, 1],  res4[,5], lwd = 1.5)
mtext("Std. Value", side = 2, line = 2, cex = 0.9)
mtext("Time (ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "G", pos = 4, font = 2, cex = 1.5)

# --- Panel H ---
ylim1 = c(min( res4[, 3], -1 *  res4[, 4]), max( res4[, 3],  res4[, 4]))
plot( res4[,1], res4[,3], col = "blue", cex = 0.5, cex.lab = 1.2, 
     cex.main = 1.3, xlab = "", ylab = "", 
     main = "Envelope (red); Filtered Data (blue)", ylim = ylim1)
lines( res4[,1], res4[,3], col = "blue")
lines( res4[,1], res4[,4], col = "red")
lines( res4[,1], -1 * res4[,4], col = "red")
abline(h = 0, col = "black", lty = 3)
mtext("Std. Value", side = 2, line = 2, cex = 0.9)
mtext("Time (ka)", side = 1, line = 2.3, cex = 0.8)
usr <- par("usr")
text(usr[1], usr[4] - 0.05*(usr[4]-usr[3]), "H", pos = 4, font = 2, cex = 1.5)

Figure 3. TimeOpt results. A. Fit: r²[envelope] (red) and r²[power] (grey) for TimeOpt run 405 vs 100-kyr ecc. B. Optimal Fit: r²[opt] for TimeOpt run 405 vs 100-kyr ecc. C. Envelope (red); Reconstructed Ecc. Model (black) for TimeOpt run 405 vs 100-kyr ecc. D. Envelope (red); Filtered Data (blue) for TimeOpt run 405 vs 100-kyr ecc. E. Fit: r²[envelope] (red) and r²[power] (grey) for TimeOpt run 100-kyr eccentricity versus precession. F. Optimal Fit: r²[opt] for TimeOpt run 100-kyr eccentricity versus precession. G. Envelope (red); Reconstructed Ecc. Model (black) for TimeOpt run 100-kyr ecc. versus precession. H. Envelope (red); Filtered Data (blue) for TimeOpt run 100-kyr eccentricity versus precession.

3.2 Extract the 405-kyr cycle and generate a sedimentation-rate curve

Based on the TimeOpt results, the 405-kyr eccentricity cycle is extracted from the induration record using a Taner bandpass filter, which spans from 4.86 to 16.2, corresponding to the peak of the optimal sedimentation rate range (1.2-4 cm/ka) as determined by the TimeOpt analysis results. Next, a sedimentation-rate curve is constructed using the minimal tuning technique, in which the distance between two successive peaks of the extracted cycle is set to the duration of the interpreted astronomical cycle. This duration is subsequently used to generate the sedimentation-rate curve (see Figure 4).

Code
induration_filt <-
  taner(
    induration,
    fhigh = 1 / (4.5 * 4.05 ),
    flow = 1 /  (1.2 * 4.05 ),
    roll = 10 ^ 5,
    xmax = 1,genplot=FALSE,verbose = FALSE
  )

induration_filt_min_tun <- minimal_tuning(
  data = induration_filt,
  pts = 5,
  cycle = 405,
  tune_opt = "minmax",
  output = 0,
  genplot = FALSE,
  keep_editable = FALSE
)


layout.matrix <- matrix(c(1, 2), nrow = 2, ncol = 1)
graphics::layout(mat = layout.matrix,
                 heights = c(1,1,1,1),
                 widths = c(1))
par(mar = c(4, 4, 1, 1))

plot(
  x = induration[, 1],
  y = induration[, 2],
  type = "l",
  main = "Induration and 405-kyr ecc. cycle depth domain",
  xlab = "position (m)",
  ylab = "Induration"
)
lines(induration_filt, col = "red",lwd=2)

usr <- par("usr")
text(
  usr[1],
  usr[4] - 0.2 * (usr[4] - usr[3]),
  "A",
  pos = 4,
  font = 2,
  cex = 1.5
)


plot(
  x = induration_filt_min_tun[, 1],
  y = induration_filt_min_tun[, 3],
  type = "l",
  xlab = "position (m)",
  ylab = "cm/kyr (kyr)",
  main = "sedimentation rate plot"
)
usr <- par("usr")
text(
  usr[1],
  usr[4] - 0.2 * (usr[4] - usr[3]),
  "B",
  pos = 4,
  font = 2,
  cex = 1.5
)

Figure 4. Minimal tuning. A. Induration record and the extracted 405-kyr eccentricity cycle. B. Sedimentation rate and sedimentation rate (cm/kyr) based on the minimal tuning.

3.3 Tracking the period (m) of the 405-kyr eccentricity cycle in the the CWT scalogram

The sedimentation rate curve attained from the minimal tuning was then used to convert cm/kyr to period (m) of different astronomical cycles. These cycles were then plotted on top of the CWT scalogram (see figure 4). We can see that the curves plot through areas of high spectral power corresponding to astronomical cycles that should be present according to the TimeOpt-derived sedimentation rate modelling runs. Next, the period (m) of the 405-kyr eccentricity cycle was tracked in the CWT.

Code
induration_wt <- analyze_wavelet(
  data = induration,
  dj = 1/250,
  lowerPeriod = 0.05,
  upperPeriod = 35,
  verbose = FALSE,
  omega_nr = 6,
  pval = FALSE,
  n_simulations = 10,
  run_multicore = FALSE
)

This code must be run manually in an interactive R session. It requires selecting points interactively

induration_track <- track_period_wavelet(induration_wt)

induration_track_comp <- completed_series( tracked_curve = induration_track, wavelet = induration_wt )

induration_track_comp <- noLow( induration_track[, c(1,2)], output = 2, smooth = 0.1 )

write.csv(induration_track_comp, “induration_track_comp.csv”)

Code
# Load the tracked period curve 
induration_track_comp <- read.csv(
  "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/induration_track_comp.csv"
)
#induration_track_comp <- read.csv("induration_track_comp.csv")


induration_track_comp <- induration_track_comp[,c(2,3)]

plot_wavelet(induration_wt,add_avg = TRUE,keep_editable = TRUE,
              dev_new = FALSE,
             periodlab = "Period (metres)",
  x_lab = "Position (metres)")

lines(induration_filt_min_tun[,1],
      log2(induration_filt_min_tun[,3]*4.05),
      col=adjustcolor("red", alpha.f=1), lwd=2)

lines(induration_track_comp[,1],
      log2(induration_track_comp[,2]/4.05*1.1),
      col=adjustcolor("purple", alpha.f=0.5), lwd=10, lty=1)

lines(induration_track_comp[,1],
      log2(induration_track_comp[,2]/4.05*0.30),
      col=adjustcolor("blue", alpha.f=0.5), lwd=10, lty=1)

lines(induration_track_comp[,1],
      log2(induration_track_comp[,2]/4.05*0.19),
      col=adjustcolor("darkgreen", alpha.f=0.5), lwd=10, lty=1)

lines(induration_track_comp[,1],
      log2(induration_track_comp[,2]),
      col=adjustcolor("black", alpha.f=0.5), lwd=10, lty=1)

Figure 5. The Continuous Wavelet Transform (CWT) of the induration record extracted from the litholog of Frýda et al. (2020) and the period (m) of astronomical cycles based on the sedimentation rates attained from the minimal tuning and the tracked period (m) of the 405-kyr eccentricity cycle. Black dotted = tracked period (m) the 405-kyr eccentricity cycle Red = the 405-kyr eccentricity cycle (minimal tuning based) Purple = the 100-kyr eccentricity cycle (minimal tuning based) Blue = the obliquity cycle (minimal tuning based) Dark green = the precession cycle (minimal tuning based)

3.4 Combine the tracked period (m) and a tie-point curve to create an anchored astrochronoloy

The tracked period (m) curve of the 405-kyr eccentricity cycle was then be used to create a floating astrochronology with an assigned uncertainty based on the analytical uncertainty of the Wavelet (Arts et al., 2024). This floating age model with uncertainty was then anchored to the astrochronologically calibrated age of the Ludlow-Pridoli boundary (423.03 ±0.53 Ma) (Arts et al., 2024). This anchored numerical age model could then be used to assign ages and durations (with uncertainty) to the record of the Kosov quarry section and its subsections

Code
induration_track_comp_unc <- wavelet_uncertainty(
  tracked_cycle = induration_track_comp,
  period_of_tracked_cycle = 405,
  wavelet = induration_wt,
  multi = 1,
  verbose = FALSE,
  genplot_time = TRUE,
  genplot_uncertainty = FALSE,
  genplot_uncertainty_wt = FALSE,
  keep_editable = FALSE,
  palette_name = "rainbow",
  color_brewer = "grDevices")

induration_track_comp_unc_sel <- induration_track_comp_unc[,c(1,3,5)]

proxy_list <- list(induration)
id <- c("age_prid_bound")
ages <- c(423030)
ageSds <- c(530/2)
ages_unc_dist <- c("n")
position <- c(22.0)
anchor_thick <- c(0.1)
anchor_thick_unc_dist <- c("u")
bound_age <-
 as.data.frame(
   cbind(
     id,
     ages,
     ageSds,
     ages_unc_dist,
     position,
     anchor_thick,
     anchor_thick_unc_dist
   )
 )

gap_dur = c(NULL)
gap_unc = c(NULL)
gap_depth = c(NULL)
gap_unc_dist = c(NULL)

gap_constraints_none <-
 as.data.frame(cbind(gap_dur, gap_unc, gap_depth, gap_unc_dist))

cycles_checks <- c(405,110,33,19)
uncer_cycles_checks <- c(20.25,35,7,6)


# curve2time_unc_anchor_res <-
#   curve2time_unc_anchor(
#  age_constraint = bound_age ,
#  tracked_cycle_curve = induration_track_comp_unc_sel,
#  tracked_cycle_period = 405 ,
#  tracked_cycle_period_unc = 6,
#  tracked_cycle_period_unc_dist = "n" ,
#  n_simulations = 10000,
#  gap_constraints = NULL,
#  proxy_data = proxy_list,
#  cycles_check = cycles_checks,
#  uncer_cycles_check = uncer_cycles_checks ,
#  max_runs = 10000 ,
#  run_multicore = TRUE,
#  verbose = TRUE ,
#  genplot = FALSE,
#  keep_nr = 2,
#  keep_all_time_curves = FALSE,
#  dj = 1 / 50 ,
#  lowerPeriod = 10 ,
#  upperPeriod = 800 ,
#  omega_nr = 10,
#  seed_nr = 1337,
#  dir = FALSE)
# saveRDS(curve2time_unc_anchor_res, file = "D:/Phd/documents/kosov/curve2time_unc_anchor_res.rds")

This code takes a long time to run

proxy_list <- list(induration) id <- c(“age_prid_bound”) ages <- c(423030) ageSds <- c(530/2) ages_unc_dist <- c(“n”) position <- c(22.0) anchor_thick <- c(0.1) anchor_thick_unc_dist <- c(“u”) bound_age <- as.data.frame( cbind( id, ages, ageSds, ages_unc_dist, position, anchor_thick, anchor_thick_unc_dist ) )

gap_dur = c(NULL) gap_unc = c(NULL) gap_depth = c(NULL) gap_unc_dist = c(NULL)

gap_constraints_none <- as.data.frame(cbind(gap_dur, gap_unc, gap_depth, gap_unc_dist))

cycles_checks <- c(405,110,33,19) uncer_cycles_checks <- c(40.5,30,5,5)

curve2time_unc_anchor_res <- curve2time_unc_anchor( age_constraint = bound_age , tracked_cycle_curve = induration_track_comp_unc_sel, tracked_cycle_period = 405 , tracked_cycle_period_unc = 6, tracked_cycle_period_unc_dist = “n” , n_simulations = 10000, gap_constraints = NULL, proxy_data = proxy_list, cycles_check = cycles_checks, uncer_cycles_check = uncer_cycles_checks , max_runs = 1000 , run_multicore = TRUE, verbose = TRUE , genplot = FALSE, keep_nr = 2, keep_all_time_curves = FALSE, dj = 1 / 100 , lowerPeriod = 10 , upperPeriod = 800 , omega_nr = 10, seed_nr = 1337, dir = FALSE) saveRDS(curve2time_unc_anchor_res, file = “curve2time_unc_anchor_res.rds”)

Code
curve2time_unc_anchor_res <- readRDS(file = "D:/Phd/documents/kosov/curve2time_unc_anchor_res.rds")

results <- as.data.frame(curve2time_unc_anchor_res[[2]])

kosov_time<- cbind(curve2time_unc_anchor_res[[1]],rowMeans(results))
kosov_sd<- rowSds(as.matrix(results))

induration_time<- tune(induration,kosov_time,extrapolate=F,genplot=F,check=T,verbose=T)
kosov_d13Ccarb_time<- tune(kosov_d13Ccarb,kosov_time,extrapolate=F,genplot=F,check=T,verbose=T)
kosov_d13Corg_time<- tune(kosov_d13Corg,kosov_time,extrapolate=F,genplot=F,check=T,verbose=T)

d13C_tuned <- kosov_d13Ccarb_time
d13C_tuned <- linterp(d13C_tuned,1,genplot=FALSE)
d13C_tuned_2 <- d13C_tuned
d13C_tuned <- noLow(d13C_tuned[,c(1,2)],smooth=0.05,2,genplot=FALSE)
diff_d13C <- diff(d13C_tuned[,2])
d13C_tuned[,3] <- c(-1*diff_d13C[1],-1*diff_d13C)
Code
plot(kosov_time[,1],kosov_time[,2]/1000,xlab="postion (m)",ylab="Time (Ma)",type="l",ylim=rev(c(min(kosov_time[,2]-2*kosov_sd)/1000,max(kosov_time[,2]+2*kosov_sd)/1000)))
lines(kosov_time[,1],(kosov_time[,2]+2*kosov_sd)/1000,col="blue")
lines(kosov_time[,1],(kosov_time[,2]-2*kosov_sd)/1000,,col="red")
abline(v=my_data[8,2],lty=3,col="red",lwd=3)

Figure 6. Astrochronological numerival age- depth model.The black line is the mean age-depth curve, and the red and blue lines are time plus and minus two standard deviations (2σ)

3.5 Ages and durations for intervals including uncertainty

The age model provides durations with uncertainty for the chrono, litho-, and event-stratigraphic intervals previously identified in the succession (Frýda and Manda, 2013; Frýda et al., 2021). The calculated ages, durations, and their associated uncertainties were rounded to the nearest 10 kyr to align with the precision of the anchor point.

Code
kosov_time<- cbind(curve2time_unc_anchor_res[[1]],rowMeans(results))
kosov_sd<- rowSds(as.matrix(results))


for(i in 1:nrow(my_data)){

row_nr_1 <- DescTools::Closest(kosov_time[,1],my_data[i,2],which = TRUE)
row_nr_2 <- DescTools::Closest(kosov_time[,1],my_data[i,3],which = TRUE)

my_data[i,4] <- round(mean(as.matrix(results[row_nr_1,]),na.rm = TRUE)/10,0)/100

my_data[i,5] <- round(2*sd(as.matrix(results[row_nr_1,]),na.rm = TRUE)/10,0)*10

my_data[i,6]<- round(mean(as.matrix(results[row_nr_2,]),na.rm = TRUE)/10,0)/100

my_data[i,7] <- round(2*sd(as.matrix(results[row_nr_2,]),na.rm = TRUE)/10,0)*10

my_data[i,8] <- round(mean(as.matrix(na.omit(results[row_nr_1,]-results[row_nr_2,])))/10,0)*10

my_data[i,9] <- round(2*sd(results[row_nr_2,]-results[row_nr_1,],na.rm = TRUE)/10,0)*10

}

for(i in 1:nrow(my_data)){
  for( j in 4:ncol(my_data)){
    if (my_data[i,j]<=10){
    my_data[i,j] <- 10
  }}}

bottom_record <-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[1,2],which=TRUE),2]
base_pridoli <-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[8,2],which=TRUE),2]
Top_record <-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[1,3],which=TRUE),2]
base_LAU<-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[4,2],which=TRUE),2]
base_S_zone<-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[5,2],which=TRUE),2]
base_F_zone<-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[6,2],which=TRUE),2]
Top_F_zone<-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[6,3],which=TRUE),2]
base_event <-  kosov_time[DescTools::Closest(kosov_time[,1],my_data[2,2],which=TRUE),2]

# Create gt table with header and custom column label (no pipe)
tab <- gt(my_data)
tab <- cols_label(tab,
                  position_bottom = "position (m) bottom",
                  position_top    = "position (m) top",
                  age_bottom     = "Age bottom (Ma)",
                  uncertainty_age_bottom="uncertainty bottom (kyr) +/-2sd ",
                  age_top = "Age top (Ma)",
                  uncertainty_age_top= "uncertainty top (kyr) +/-2sd ",
                  duration        = "duration (kyr)",
                  uncertainty     = "uncertainty in duration (kyr) +/-2sd ")
tab <- tab_header(tab, title = "Table 1. Ages and durations based on the anchored astrochronological age model")

tab
Table 1. Ages and durations based on the anchored astrochronological age model
Interval position (m) bottom position (m) top Age bottom (Ma) uncertainty bottom (kyr) +/-2sd Age top (Ma) uncertainty top (kyr) +/-2sd duration (kyr) uncertainty in duration (kyr) +/-2sd
Entire record -7.2 25.0 424.60 560 422.80 530 1800 190
Lau biogeochemical Event -0.4 20.0 424.06 550 423.19 530 870 90
LKB Event (trace metal peak) -0.4 0.0 424.06 550 424.03 550 30 10
R-zone 0.0 1.4 424.03 550 423.94 540 90 10
S-Zone 1.4 11.6 423.94 540 423.64 540 310 30
F-Zone 11.6 20.0 423.64 540 423.19 530 440 50
Ludlow part section 0.0 22.0 424.03 550 423.03 530 1000 110
Pridoli part section 22.0 25.0 423.03 530 422.80 530 230 20

3.6 Extract and plot the record and the astronomical cycles in the time domain

The age model was used to convert the induration and δ13Ccarb record to the (numerical) time domain. Next, the CWT was performed on the tuning induration record. Peaks corresponding to the 8.5-kyr half precession, ~16-kyr precession, 28-kyr bliquity,50-kyr eccentricity, 100-kyr eccentricity, 200-kyr eccentricity, 405-kyr eccentricity and the 1.6-Myr eccentricity cycle can be observed in the CWT scalogram. The precession, obliquity, 100-kyr eccentricity and 405-kyr eccentricity were also extracted from the record. The Hilbert transform was applied to the precession, obliquity and 100-kyr eccentricity cycles. The 405-kyr eccentricity cycle was also extracted from the Hilbert transform-derived amplitude record of the 100-kyr eccentricity and compared to the 405-kyr eccentricity directly extracted from the record, showing an anti-phased relationship.

Code
induration_time <- linterp(induration_time,genplot=FALSE)
induration_time_wt <- analyze_wavelet(data = induration_time,
                                      dj=1/200,upperPeriod = 2400,
                                      lowerPeriod = 5,omega_nr = 6)
induration_filt_time_405 <-
  taner(
    induration_time,
    fhigh = 1 / 505,
    flow = 1 /  305,
    roll = 10 ^ 20,
    xmax = 1/50,genplot=FALSE
  )

induration_filt_time_110 <-
  taner(
    induration_time,
    fhigh = 1 / 150,
    flow = 1 /  85,
    roll = 10 ^ 20,
    xmax = 1/50,genplot=FALSE
  )

induration_filt_time_110_hilb <-
  hilbert(induration_filt_time_110,genplot=FALSE
  )


induration_filt_time_110_hilb_405 <-
  taner(
    induration_filt_time_110_hilb,
    fhigh = 1 / 505,
    flow = 1 /  305,
    roll = 10 ^ 20,
    xmax = 1/50,genplot=FALSE
  )



induration_filt_time_obl <-
  taner(
    induration_time,
    fhigh = 1 / (33.5+7.5),
    flow = 1 / (33.5-7.5),
    roll = 10 ^ 10,
    xmax = 1/10,genplot=FALSE
  )
induration_filt_time_obl_hilb <- hilbert(induration_filt_time_obl,genplot=FALSE)
induration_filt_time_obl_hilb_173 <-
  taner(
    induration_filt_time_obl_hilb,
    fhigh = 1 / (173+20),
    flow = 1 / (173-20),
    roll = 10 ^ 20,
    xmax = 1/100,genplot=FALSE
  )

induration_filt_time_prec <-
  taner(
    induration_time,
    fhigh = 1 / (20+5),
    flow = 1 / (20-5),
    roll = 10 ^ 20,
    xmax = 1/10,genplot=FALSE
  )
induration_filt_time_prec_hilb <- hilbert(induration_filt_time_prec,genplot=FALSE)
induration_filt_time_prec_hilb_110 <-
  taner(
    induration_filt_time_prec_hilb,
    fhigh = 1 / 150,
    flow = 1 /  85,
    roll = 10 ^ 20,
    xmax = 1/75,genplot=FALSE
  )
Code
Ludlow_col <- geo_col(name = "Ludlow")
Pridoli_col <- geo_col(name = "Pridoli")
    
Top_record  <- max(kosov_time[,2])
bottom_record <- min(kosov_time[,2])
  
ylims <- rev(c(min(kosov_time[, 2])-5,max(kosov_time[,2])+5))
vert_lines <-   c(8.5,16,28,50,105,200,405,1600)



induration_time <- linterp(induration_time,genplot=FALSE)
induration_time_wt <- analyze_wavelet(data = induration_time,
                                      dj=1/200,upperPeriod = 2400,
                                      lowerPeriod = 5,omega_nr = 6)

{
layout.matrix <- matrix(c(rep(0,4),1,rep(0,4),seq(2,10,by=1)),
                        nrow = 2,
                        ncol = 9 ,
                        byrow = TRUE)

graphics::layout(mat = layout.matrix,
                 heights = c(0.25,1),
                 # Heights of the two rows
                 widths = c(rep(c(2,1,3,3,6,3,3,3),2)))

par(mar = c(0, 0.5, 1, 0.5))


wavelet <- induration_time_wt
xlim_vals = rev(c(min(wavelet$x), max(wavelet$x)))
ylim_vals = c(5,2400)
n.levels <- 100

color_brewer_Sel <-
  "grDevices::rainbow(n=n.levels, start = 0, end = 0.7)"
key.cols <- rev(eval(parse(text = color_brewer_Sel)))
power_max_mat.levels = quantile(wavelet$Power, probs = seq(
  from = 0,
  to = 1,
  length.out = n.levels + 1
))


periodlab <- "period (kyr)"
main = NULL

plot(
  wavelet$Period,
  wavelet$Power.avg,
  typ = "l",
  log = "x",
  xlim = ylim_vals,
  xaxt = 'n',
  xaxs = "i"
)

abline(v = vert_lines, col=adjustcolor("black", alpha.f=0.4), lwd=6, lty=1)


text(x=6.5,
     y=0.01,
  labels="E",
  cex =2
)


par(mar = c(4, 4, 0, 0))


plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "Age (Ma)",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt= "n",
  ylim = ylims,
)    

par(xpd = NA)
title(main = "A", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

a <- round(c(max(kosov_time[, 2]), min(kosov_time[, 2]))/1000)

axis(2, at = rev(1000*seq(a[1],
                          a[2]-0.25, by = -0.25)),
     labels = rev(seq(a[1],
                      a[2]-0.25, by = -0.25)))

Hmisc::minor.tick(nx = 0, ny = 10,   # Ticks density
                  tick.ratio = 0.5) # Ticks size


polygon(
    y = c(
      bottom_record,
      bottom_record,
      base_pridoli,
      base_pridoli
    ),
    x = c(0, 1, 1, 0),
    col = Ludlow_col
  )

    polygon(
    y = c(
      base_pridoli,
      base_pridoli,
      Top_record,
      Top_record
    ),
    x = c(0, 1, 1, 0),
    col = Pridoli_col
  )

    text(
    labels = "Ludlow",cex=2,
    x = 0.5,
    srt = 270,
    y = (bottom_record + base_pridoli) / 2
  )

    text(
    labels = "Pridoli",cex=2,
    x = 0.5,
    srt = 270,
    y = (Top_record + base_pridoli) / 2
  )
 
   
par(mar = c(4, 0.5, 0, 0.5))

    
plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt = "n",
  ylim = ylims
)    

par(xpd = NA)
title(main = "B", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

polygon(
    y = c(
      base_LAU,
      base_LAU,
      base_S_zone,
      base_S_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
      labels = "R-zone",cex=1,
    x = 0.5,
    srt = 270,
    y = (base_LAU + base_S_zone) / 2
  )

polygon(
    y = c(
      base_S_zone,
      base_S_zone,
      base_F_zone,
      base_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "S-zone",cex=2,
    x = 0.5,
    srt = 270,
    y = (base_S_zone + base_F_zone) / 2
  )

polygon(
    y = c(
      base_F_zone,
      base_F_zone,
      Top_F_zone,
      Top_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "F-zone",cex=2,
    x = 0.5,
    srt = 270,
    y = (Top_F_zone + base_F_zone) / 2
  )

 plot(
  d13C_tuned_2[, 2],
  d13C_tuned_2[, 1],
  type = "l",
  ylim = ylims,
  xlab =  "δ13Ccarb",
  yaxt = "n",
  xaxs = "i",
  xlim = c(min(d13C_tuned_2[, 2]) - 0.15, max(d13C_tuned_2[, 2]) *
             1.1),
  yaxs = "i",col="lightgreen"
)

 par(xpd = NA)
title(main = "C", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset
 
 
polygon(
  x =
    c(
      min(d13C_tuned_2[, 2]) - 0.15,
      max(d13C_tuned_2[, 2]) * 1.1,
      max(d13C_tuned_2[, 2]) * 1.1,
      min(d13C_tuned_2[, 2]) - 0.15
    ),
  y = c(
    Top_F_zone,
    Top_F_zone,
    base_LAU,
    base_LAU
  ),
  col = rgb(0, 0, 0, 0.25)
)

 text(
    labels = "Lau δ13Ccarb excursion",
    x = 0.5,
    srt = 270,cex=2,
    y = (Top_F_zone + base_LAU) / 2
  )
 
 
abline(h=base_event,col="red",lty=3,lwd=3) 

    text(
    labels = "Onset \n LKB",
    x = 4,cex=2,
    srt = 0,
    y = (base_event + 5)
  ) 

lines(d13C_tuned_2[,c(2,1)],col="lightgreen",lwd=1)
lines(d13C_tuned[,c(2,1)],col="darkgreen",lwd=1)



plot(
  induration_time[, 2],
  induration_time[, 1],
  type = "l",
  ylim =ylims,
  yaxs = "i",
  yaxt = "n",
  xlab = "Induration"
)

par(xpd = NA)
title(main = "D", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset
image(
  y = wavelet$x,
  x = wavelet$axis.2,
  z = (wavelet$Power),
  col = key.cols,
  breaks = power_max_mat.levels,
  useRaster = TRUE,
  xlab = periodlab,
  ylab = "",
  #axes = FALSE,
  #yaxt = "n" ,
  xaxt = "n" ,
  yaxt = "n" ,
  main = main,
  ylim = ylims,
  xlim = log2(ylim_vals)
)

periodtck = 0.02
periodtcl = 0.5
main = NULL
lwd = 2
lwd.axis = 1
box(lwd = lwd.axis)

period.tick = unique(trunc(wavelet$axis.2))
period.tick <- period.tick[period.tick >= log2(ylim_vals[1])]
period.tick <- period.tick[period.tick <= log2(ylim_vals[2])]
nrs <- seq(period.tick[1], length(period.tick), by = 2)
period.tick <- nrs
period.tick[period.tick < log2(wavelet$Period[1])] = NA
period.tick = na.omit(period.tick)
period.tick.label = 2 ^ (period.tick)

axis(
  1,
  lwd = lwd.axis,
  at = period.tick,
  labels = NA,
  tck = periodtck,
  tcl = periodtcl
)


mtext(
  period.tick.label,
  side = 1,
  at = period.tick,
  las = 2,
  line = par()$mgp[2] - 0.5,
  font = par()$font.axis,
  cex = par()$cex.axis
)

abline(v = log2(vert_lines), col=adjustcolor("black", alpha.f=0.4), lwd=6, lty=1)

plot(
  x = induration_filt_time_405[, 2],
  y = induration_filt_time_405[, 1],
  type = "l",
  ylim =ylims,
,
  yaxs = "i",
  yaxt = "n",
  xlab = "405-kyr ecc"
)

par(xpd = NA)
title(main = "F", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

lines(x = (induration_filt_time_110_hilb_405[, 2]
            -mean(induration_filt_time_110_hilb_405[, 2]))*2+mean(induration_filt_time_405[,2]), y = induration_filt_time_110_hilb_405[, 1], col =
        "red")

plot(
  x = induration_filt_time_110[, 2],
  y = induration_filt_time_110[, 1],
  type = "l",
  ylim =ylims,
,
  yaxs = "i",
  yaxt = "n",
  xlab = "100-kyr ecc")

par(xpd = NA)
title(main = "G", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

lines(
  induration_filt_time_110_hilb[, 2] + mean(induration_filt_time_110[, 2]),
  induration_filt_time_110_hilb[, 1],
  lwd = 1.5,col="red"
)



plot(
  x = induration_filt_time_prec[, 2],
  y = induration_filt_time_prec[, 1],
  type = "l",
  ylim =ylims,
,
  yaxs = "i",
  yaxt = "n",
  xlab = "Precession"
)

par(xpd = NA)
title(main = "H", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

lines(
  induration_filt_time_prec_hilb[, 2] + mean(induration_filt_time_prec[, 2]),
  induration_filt_time_prec_hilb[, 1],
  lwd = 1.5,col="red"
)


plot(
  x = induration_filt_time_obl[, 2],
  y = induration_filt_time_obl[, 1],
  type = "l",
  ylim =ylims,
,
  yaxs = "i",
  yaxt = "n",
  xlab = "Obliquity"
)

par(xpd = NA)
title(main = "I", line = 1, cex.main = 1.5)
par(xpd = FALSE) # reset

lines(
  induration_filt_time_obl_hilb[, 2] + mean(induration_filt_time_obl[, 2]),
  induration_filt_time_obl_hilb[, 1],
  lwd = 1.5,col="red"
)
}

Figure 7. Induration proxy record in the time domain, including the wavelet scalogram of the induration record and astronomical cycles extracted from said record. A Stages. B. Event zone subdivision C. The δ13Ccarb record. D. The Induration record. E. Wavelet scalogram of the induration record with the average spectral power on top. The black vertical lines in the wavelet scalograms are durations of known astronomical cycles. From left to right, these cycles are the 8.5-kyr half precession, ~16-kyr precession, 28-kyr obliquity,50-kyr eccentricity, 100-kyr eccentricity, 200-kyr eccentricity, 405-kyr eccentricity and the 1.6-Myr eccentricity cycle. F. The black line is a 405-kyr eccentricity cycle extracted from the Induration record. The red line is the 405-kyr eccentricity cycle extracted from the Hilbert transform of the 100-kyr eccentricity cycle of the Induration record. G. The 100-kyr eccentricity cycle was extracted from the Induration record (black line) and the Hilbert transform of the 100-kyr eccentricity cycle (red line). H.) Obliquity cycle extracted from the Induration record (black line), and the Hilbert transform of the obliquity cycle (red line). I. The precession cycle extracted from the Induration record (black line) and the Hilbert transform of the precession cycle (red line).

3.7 The phase relationships

The 405-kyr eccentricity cycle was extracted from the induration record and extracted from the Hilbert transform of the 100-kyr eccentricity cycle of the induration record. The 100-kyr eccentricity cycle was extracted from the induration record and extracted from the Hilbert transform of the precession cycle of the induration record. The relationship between the 405-kyr eccentricity cycle directly extracted from the record and the one obtained from the Hilbert transform of the 100-kyr cycle shows an anti-phased relationship. The relationship between the 100-kyr eccentricity cycle directly extracted from the induration record and that obtained from the Hilbert transform of the precession cycle demonstrates a generally anti-phased relationship as well.

Code
ylims <- c(min(kosov_time[, 2])-5,max(kosov_time[,2])+5)
ylims <- rev(ylims/1000)
layout.matrix <- matrix(c(1,2),
                        nrow = 1,
                        ncol = 2 ,
                        byrow = TRUE)

graphics::layout(mat = layout.matrix,
                 heights = c(1,1),
                 # Heights of the two rows
                 widths = c(1,1))

par(mar = c(4, 4, 4, 2))

plot(
  x = induration_filt_time_405[, 2],
  y = induration_filt_time_405[, 1]/1000,
  type = "l",
  ylim =ylims,
  xlab = "405-kyr ecc",col="black",
  ylab = "Age (Ma)",
  main="A"
)

lines(x = (induration_filt_time_110_hilb_405[, 2]
            -mean(induration_filt_time_110_hilb_405[, 2]))*2+mean(induration_filt_time_405[,2]), y = induration_filt_time_110_hilb_405[, 1]/1000, col =
        "red")

plot(
  x = induration_filt_time_110[, 2],
  y = induration_filt_time_110[, 1]/1000,
  type = "l",
  ylim =ylims,
  xlab = "100-kyr ecc",col="black",
  ylab =  "Age (Ma)",
  main="B"
)

lines(x = (induration_filt_time_prec_hilb_110[, 2]
            -mean(induration_filt_time_prec_hilb_110[, 2]))*2+mean(induration_filt_time_110[,2]), y = induration_filt_time_prec_hilb_110[, 1]/1000, col =
        "red")

Figure 8. Astronomical cycles extracted from the tuned induration record. A. The black line is a 405-kyr eccentricity cycle extracted from the Induration record. The red line is the 405-kyr eccentricity cycle extracted from the Hilbert transform of the 100-kyr eccentricity cycle of the Induration record. B. The black line is a 100-kyr eccentricity cycle extracted from the Induration record. The red line is the 100-kyr eccentricity cycle extracted from the Hilbert transform of the precession cycle of the Induration record.

3.8 Rate of change in the δ13Ccarb curve

The rate of change (‰/kyr) of the δ13Ccarb curve was calculated from the LOWESS regression applied to the δ¹³Ccarb record. The regression was applied to minimise the influence of short-term local noise (Figure 9). The rate of change (‰/kyr) reaches a maximum of 0.09 (‰/kyr) during the onset of the Lau biogeochemical Event. The rate of change (‰/kyr) record contains a strong imprint of the 100-kyr eccentricity cycle, which was subsequently extracted from the record and plotted next to the 100-kyr eccentricity cycle extracted from the induration record. During the Lau, the rate of change (‰/kyr) recorded its largest amplitude variations. The 100-kyr eccentricity cycle extracted from the rate of change (‰/kyr) record is generally anti-phased with the 100-kyr eccentricity cycle extracted from the induration record.

Code
d13C_tuned_diff_110 <-
  astrochron::taner(
     astrochron::linterp(d13C_tuned[,c(1,3)],genplot=FALSE,verbose=FALSE),
    fhigh = 1 / 150,
    flow = 1 /  80,
    roll = 10 ^ 6,
    xmax = 1/50,
    genplot=FALSE
  )
kosov_d13Ccarb_time_110 <-
  astrochron::taner(
    astrochron::noLow(astrochron::linterp(kosov_d13Ccarb_time[,c(1,2)],    genplot=FALSE
),smooth=0.25,1,    genplot=FALSE),
    fhigh = 1 / 150,
    flow = 1 /  80,
    roll = 10 ^ 6,
    xmax = 1/50,
        genplot=FALSE

  )

layout.matrix <- matrix(c(1, 2,3,4,5,6), nrow = 1, ncol = 6)
graphics::layout(mat = layout.matrix,
                 heights = c(1,1,1,1,1,1), # Heights of the two rows
                 widths = c(1,1,2,2,2,2)) # Widths of the two columns

par(mar = c(4, 4, 2, 0))

a <- round(c(max(kosov_time[, 2]), min(kosov_time[, 2]))/1000)

ylims <- c(min(kosov_time[, 2])-5,max(kosov_time[,2])+5)
ylims <- rev(ylims)

plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "Age (Ma)",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt= "n",
  ylim = ylims,
  main="A"
)    

axis(2, at = rev(1000*seq(a[1],
                          a[2]-0.25, by = -0.25)),
     labels = rev(seq(a[1],
                      a[2]-0.25, by = -0.25)))

Hmisc::minor.tick(nx = 0, ny = 10,   # Ticks density
                  tick.ratio = 0.5) # Ticks size

polygon(
    y = c(
      bottom_record,
      bottom_record,
      base_pridoli,
      base_pridoli
    ),
    x = c(0, 1, 1, 0),
    col = Ludlow_col
  )

    polygon(
    y = c(
      base_pridoli,
      base_pridoli,
      Top_record,
      Top_record
    ),
    x = c(0, 1, 1, 0),
    col = Pridoli_col
  )

    text(
    labels = "Ludlow",
    x = 0.5,
    srt = 270,
    y = (bottom_record + base_pridoli) / 2
  )

    text(
    labels = "Pridoli",
    x = 0.5,
    srt = 270,
    y = (Top_record + base_pridoli) / 2
  )
 
   
par(mar = c(4, 0.5, 2, 0.5))

plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt = "n",
  ylim = ylims,
   main="B"
)    


polygon(
    y = c(
      base_LAU,
      base_LAU,
      base_S_zone,
      base_S_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "R-zone",
    x = 0.5,
    srt = 0,
    y = (base_LAU + base_S_zone) / 2
  )

polygon(
    y = c(
      base_S_zone,
      base_S_zone,
      base_F_zone,
      base_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "S-Zone",
    x = 0.5,
    srt = 0,
    y = (base_S_zone + base_F_zone) / 2
  )

polygon(
    y = c(
      base_F_zone,
      base_F_zone,
      Top_F_zone,
      Top_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "F-zone",
    x = 0.5,
    srt = 0,
    y = (Top_F_zone + base_F_zone) / 2
  )
    
    
plot(
  induration_time[, 2],
  induration_time[, 1],
  type = "l",
  ylim =ylims,
  yaxs = "i",
  yaxt = "n",
  xlab = "Induration",
   main="C"

)
    
plot(
  d13C_tuned_2[, 2],
  d13C_tuned_2[, 1],
  type = "l",
  ylim = ylims,
  xlab =  "δ13Ccarb",
  yaxt = "n",
  xaxs = "i",
  xlim = c(min(d13C_tuned_2[, 2]) - 0.15, max(d13C_tuned_2[, 2]) *
             1.1),
  yaxs = "i",col="lightgreen",main="D"
)

polygon(
  x =
    c(
      min(d13C_tuned_2[, 2]) - 0.15,
      max(d13C_tuned_2[, 2]) * 1.1,
      max(d13C_tuned_2[, 2]) * 1.1,
      min(d13C_tuned_2[, 2]) - 0.15
    ),
  y = c(
    Top_F_zone,
    Top_F_zone,
    base_LAU,
    base_LAU
  ),
  col = rgb(0, 0, 0, 0.25)
)

 text(
    labels = "Lau  δ13Ccarb excursion",
    x = 2,
    srt = 270,
    y = (Top_F_zone + base_LAU) / 2
  )
 
 
abline(h=base_event,col="red",lty=3,lwd=3) 

    text(
    labels = "Onset \n LKB",
    x = 4,cex=2,
    srt = 0,
    y = (base_event + 5)
  ) 

lines(d13C_tuned_2[,c(2,1)],col="lightgreen",lwd=1)
lines(d13C_tuned[,c(2,1)],col="darkgreen",lwd=1)

plot(d13C_tuned[,c(3,1)],type="l",
     col="darkgreen"
     ,xlab= "δ13Ccarb rate of change (‰/kyr)",
       ylim = ylims,
     ylab="time (kyr)",yaxt="n",
       yaxs = "i", main="E")

lines(d13C_tuned_diff_110[,c(2,1)], col="darkgreen", lwd=2)

plot(induration_filt_time_110[,2], induration_filt_time_110[,1], 
      type="l", yaxt="n",xaxt="n",
      ylim=ylims, col="red", xlab="", lwd=2,
       yaxs = "i", main="F")
par(new=TRUE)
plot(d13C_tuned_diff_110[,c(2,1)], col="darkgreen", lwd=2,ylim=ylims,xlab="",
    type="l",yaxt="n",xaxt="n",  yaxs = "i")

Figure 9. Rate of change (‰/kyr) of the δ13Ccarb record. A Stages. B. Event zone subdivision. C. The Induration record. D. The δ13Ccarb record (light green), and the LOWESS smoothed curve (dark green) E. The δ13Ccarb rate of change record (‰/kyr) and the 100-kyr eccentricity cycle extracted from said record F. The 100-kyr eccentricity cycle extracted from the δ13Ccarb rate of change record (‰/kyr) (dark green) and the 100-kyr eccentricity cycle extracted from the induration record.

3.9 The lag-1 sea-level curve

The lag-1 function was used to calculate the lag-1 autocorrelation coefficient using a windowed analysis Monte-Carlo analysis. The resulting curve serves as a proxy for sea-level changes (Li et al., 2018). The lag-1 does show a few trends (see Figure 10). Before the Lau excursion, the curve is characterised by relatively high values superimposed by some low-amplitude fluctuations. At the onset of the Lau event, a minor curve rise is observed, followed by a large drop towards a minimum during the middle of the Lau δ13Ccarb excursion. Toward the top of the studied succession, the curve returns to higher and more regular values. The lag-1 record contains the imprint of the 405-kyr eccentricity cycle, which was extracted and compared to the 405-kyr eccentricity cycle extracted from the induration record. The 405-kyr eccentricity cycle extracted from the rate lag-1 record is generally anti-phased with the 405-kyr eccentricity cycle extracted from the induration record.

Code
induration <- read.csv(
  "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/induration.csv"
)
induration_2 <- sortNave(induration[,c(1,2)],genplot=FALSE)
induration_2[,1] <- seq(25,-7.20,length.out=length(induration_2[,1]))
induration_2 <- linterp(induration_2,0.01,genplot=FALSE)

induration_2[,2] <- induration_2[,2]/max(induration_2[,2])
induration_2[,2] <- sqrt(induration_2[,2])
induration_2[,2] <- induration_2[,2]-min(induration_2[,2])
induration_2[,2] <- induration_2[,2]/max(induration_2[,2])
induration_2[,2] <- induration_2[,2]*5

induration_time_2<- tune(induration_2,kosov_time,extrapolate=F,genplot=F,check=T,verbose=T)
induration_time_2 <- na.omit(induration_time_2)

# induration_time_lag_1_2 <- lag_1(
#   data = induration_time_2,
#   n_sim = 10000,
#   run_multicore = TRUE,
#   win_max = 250,
#   win_min = 10,
#   verbose = TRUE
# )
# write.csv(induration_time_lag_1_2,"D:/Phd/documents/kosov/induration_time_lag_1_2.csv")

This code takes a long time to run so one can also download the results from github induration_time_lag_1 <- lag_1( data = induration_time_2, n_sim = 1000, run_multicore = TRUE, win_max = 300, win_min = 50, verbose = TRUE ) write.csv(induration_time_lag_1,“induration_time_lag_1.csv”)

Code
 induration_time_lag_1 <- read.csv(
   "https://raw.githubusercontent.com/stratigraphy/Kosov-cyclostrat/main/induration_time_lag_1_2.csv"
 )

#induration_time_lag_1 <- read.csv("D:/Phd/documents/kosov/induration_time_lag_1_2.csv")
induration_time_lag_1 <- linterp(induration_time_lag_1[,c(2,3)],verbose=F,genplot=F)

# induration_time_lag_1_wt <- analyze_wavelet(
#   data = induration_time_lag_1,
#   dj = 1/100,
#   lowerPeriod = 50,
#   upperPeriod = 4000,
#   verbose = FALSE,
#   omega_nr = 8,
#   pval = FALSE,
#   n_simulations = 10,
#   run_multicore = FALSE
# )
# 
# plot_wavelet(induration_time_lag_1_wt,add_avg = TRUE,add_abline_h = c(125,220,405,660,1100),dev_new=FALSE)

induration_time_lag_2_110 <- taner(induration_time_lag_1[,c(1,2)],flow=1/80,fhigh=1/135,roll=10^10,xmax=1/50,detrend=TRUE,demean=TRUE,genplot=FALSE)

induration_time_lag_1_405 <-
  taner(induration_time_lag_1[,c(1,2)],
    fhigh = 1 / 505,
    flow = 1 /  305,
    roll = 10 ^ 6,
    xmax = 1/50,detrend=FALSE,demean=FALSE,genplot=FALSE)


layout.matrix <- matrix(c(1, 2,3,4,5,6), nrow = 1, ncol = 6)
graphics::layout(mat = layout.matrix,
                 heights = c(1,1,1,1,1,1), # Heights of the two rows
                 widths = c(1,1,2,2,2,2)) # Widths of the two columns

par(mar = c(4, 4, 2, 0))

ylims <- rev(c(min(kosov_time[, 2])-5,max(kosov_time[,2])+5))

plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "Age (Ma)",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt= "n",
  ylim = ylims,
  main="A"
)    


axis(2, at = rev(1000*seq(a[1],
                          a[2]-0.25, by = -0.25)),
     labels = rev(seq(a[1],
                      a[2]-0.25, by = -0.25)))

Hmisc::minor.tick(nx = 0, ny = 10,   # Ticks density
                  tick.ratio = 0.5) # Ticks size


polygon(
    y = c(
      bottom_record,
      bottom_record,
      base_pridoli,
      base_pridoli
    ),
    x = c(0, 1, 1, 0),
    col = Ludlow_col
  )

    polygon(
    y = c(
      base_pridoli,
      base_pridoli,
      Top_record,
      Top_record
    ),
    x = c(0, 1, 1, 0),
    col = Pridoli_col
  )

    text(
    labels = "Ludlow",
    x = 0.5,
    srt = 270,
    y = (bottom_record + base_pridoli) / 2
  )

    text(
    labels = "Pridoli",
    x = 0.5,
    srt = 270,
    y = (Top_record + base_pridoli) / 2
  )
 
   
par(mar = c(4, 0.5, 2, 0.5))

plot(
  x = c(0, 1),
  y = c(max(kosov_time[, 1]) + 5, min(kosov_time[, 1]) -
          5),
  col = "white",
  xlab = "",
  ylab = "",
  xaxt = "n",
  xaxs = "i",
  yaxs = "i",
  yaxt = "n",
  ylim = ylims,
    main="B"
)    


polygon(
    y = c(
      base_LAU,
      base_LAU,
      base_S_zone,
      base_S_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "R-zone",
    x = 0.5,
    srt = 0,
    y = (base_LAU + base_S_zone) / 2
  )

polygon(
    y = c(
      base_S_zone,
      base_S_zone,
      base_F_zone,
      base_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "S-Zone",
    x = 0.5,
    srt = 0,
    y = (base_S_zone + base_F_zone) / 2
  )

polygon(
    y = c(
      base_F_zone,
      base_F_zone,
      Top_F_zone,
      Top_F_zone
    ),
    x = c(0, 1, 1, 0),
    col = "lightgrey"
  )
    text(
    labels = "F-zone",
    x = 0.5,
    srt = 0,
    y = (Top_F_zone + base_F_zone) / 2
  )
    
    
plot(
  induration_time[, 2],
  induration_time[, 1],
  type = "l",
  ylim =ylims,
  yaxs = "i",
  yaxt = "n",
  xlab = "Induration",main="C"

)
    
plot(
  d13C_tuned_2[, 2],
  d13C_tuned_2[, 1],
  type = "l",
  ylim = ylims,
  xlab =  "δ13Ccarb",
  yaxt = "n",
  xaxs = "i",
  xlim = c(min(d13C_tuned_2[, 2]) - 0.15, max(d13C_tuned_2[, 2]) *
             1.1),
  yaxs = "i",col="lightgreen",main="D"
)

polygon(
  x =
    c(
      min(d13C_tuned_2[, 2]) - 0.15,
      max(d13C_tuned_2[, 2]) * 1.1,
      max(d13C_tuned_2[, 2]) * 1.1,
      min(d13C_tuned_2[, 2]) - 0.15
    ),
  y = c(
    Top_F_zone,
    Top_F_zone,
    base_LAU,
    base_LAU
  ),
  col = rgb(0, 0, 0, 0.25)
)

 text(
    labels = "Lau  δ13Ccarb excursion",
    x = 0.5,
    srt = 270,
    y = (Top_F_zone + base_LAU) / 2
  )
 
 
abline(h=base_event,col="red",lty=3,lwd=3) 

    text(
    labels = "Onset \n LKB",
    x = 4,cex=2,
    srt = 0,
    y = (base_event + 5)
  ) 

lines(d13C_tuned_2[,c(2,1)],col="lightgreen",lwd=1)
lines(d13C_tuned[,c(2,1)],col="darkgreen",lwd=1)


plot(induration_time_lag_1[,2], induration_time_lag_1[,1],
     type="l", ylim=ylims,lwd=2,yaxs = "i",yaxt="n",
     col="red", xlab="lag-1 sea-level", ylab="",main="E")

lines(induration_time_lag_1_405[,2], induration_time_lag_1_405[,1],
      type="l", col="brown", xlim=c(0,0.7),lwd=2)


plot(induration_filt_time_405[,2], induration_filt_time_405[,1], 
      type="l", yaxt="n",xaxt="n",
      ylim=ylims, col="blue", xlab="", lwd=2,
       yaxs = "i",main="F")
par(new=TRUE)
plot(induration_time_lag_1_405[,c(2,1)], col="brown", lwd=2,ylim=ylims,xlab="",
    type="l",yaxt="n",xaxt="n",  yaxs = "i")

Figure 10. Rate of change of the δ13Ccarb record. A Stages. B. Event zone subdivision. C. The Induration record. D. The δ13Ccarb record (light green) and the LOWESS smoothed curve (dark green) E. The lag-1 sea-level curve (red) and the 405-kyr eccentricity cycle extracted from said record (brown). F. The 405-kyr eccentricity cycle extracted from the lag-1 curve (brown) and the 405-kyr eccentricity cycle extracted from the Induration record (blue).

4. Discussion

4.1 The astrochronology and the imprint of astronomical cycles

The anchored astrochronological age model was used to assign ages, durations, and associated uncertainties to chrono- and geochronologic units, lithological units, and events in the Kosov quarry section, as tabulated in Table 1. The Lau biogeochemical Event started at 424.06 ± 0.55 Ma and lasted 0.87 ± 0.03 Myr; in contrast, the LKB Event was as short as 30 ± 10 kyr. The durations and associated uncertainties for the Lau Event and its subdivisions are the first astrochronological constraints available for a near-complete record spanning the Lau Event. The durations and associated uncertainties are therefore the best constrained reference values to date. A previous estimate for the onset of the Lau Event is based on the dating of a hiatus in the Cellon section, spanning 423.83 ± 0.55 Ma to 424.19 ± 0.55 Ma, with the Lau Biogeochemical Event starting within the hiatus (Arts et al., 2024). Our age for the onset of the Lau biogeochemical Event (424.06 ± 0.55 Ma) falls within this range, supporting alignment with previous results. The uncertainties assigned to durations in the record are around ~15%. Since only one proxy was used, a multi-proxy study might even further reduce the uncertainty in the underlying astrochronology of the Kosov quarry section.

The CWT in the time domain contains spectral peaks which can be linked to the 8.5-kyr half precession, 16-kyr precession, 28-kyr obliquity,50-kyr eccentricity, 100-kyr eccentricity, 200-kyr eccentricity, 405-kyr eccentricity and the 1.6-Myr eccentricity cycle (see Figure 7). The observation of a whole suite of spectral peaks corresponding to known astronomical cycles in the CWT scalogram validates the underlying age model. The periods of the obliquity and precession cycles are at the shorter end of the typically expected durations for the Silurian, yet remain within the current uncertainty range for the estimated durations of these cycles during the Silurian (Waltham, 2015; Farhat et al., 2022; Wu et al., 2024). The spectral peak of ~55 kyr was observed in the average CWT scalogram. This spectral peak can be either the 55 or 54-kyr eccentricity cycle or the 52.8-kyr obliquity cycle (Laskar et al., 2004; Laskar, 2020, p. 20) (see Figure 7). The spectral power of the 55-kyr cycle is highly variable, indicating that the 55-kyr cycle might be of a transient nature unrelated to astronomical forcing. A 200-kyr cycle can also be identified in the record. Given the strong imprint of the 100-kyr and 405-kyr eccentricity cycles, it is reasonable to interpret the 200-kyr cycle as the seldom observed 200-kyr eccentricity cycle (Hilgen et al. (2020)). The average spectral power of the induration record exhibits a peak at ~1.6 million years. This peak is most likely related to the 1.6-Myr eccentricity cycle. This peak is most likely related the 1.6-Myr eccentricity cycle Hinnov (2000); Laskar et al. (2004). Since the record is quite short (1800 kyr), its interpretation remains preliminary.

Since amplitude modulating cycles apply unidirectional to the amplitude of the signal, irrespective of whether the proxy exhibits a positive or negative response to insolation forcing it is possible to infer the phase relationship between astronomical cycles extracted from the proxy record and its relationship with the true phase of the astronomical cycle, as well as its relationship with changes in insolation (Hinnov, 2000; Laskar et al., 2004). The 405-kyr eccentricity cycle directly extracted from the record and derived from the Hilbert transform of the 100-kyr cycle shows an anti-phased relationship. The 100-kyr eccentricity cycle obtained directly from the induration record and from the Hilbert transform of the precession cycle also demonstrates a generally anti-phased relationship. This indicates that during isolation minima, induration values are at a maximum. This phase relationship is logical because during an isolation maximum, seasonality increases, leading to higher runoff, increased detrital products, and decreased carbonate productivity (lower induration) (Mutterlose and Ruffell, 1999; Martinez, 2018).

The established relationship between isolation and induration can also be used to understand the imprint of astronomical cycles in the rate of change ‰/kyr) of the δ¹³Ccarb record and the lag-1 sea-level curve. Since the 100-kyr eccentricity cycle extracted from the rate of change (‰/kyr) record is generally anti-phased with the 100-kyr eccentricity cycle extracted from the induration record, this indicates that the rate of change (‰/kyr) is the highest during eccentricity maxima. The 405-kyr eccentricity cycle extracted from the rate lag-1 record is generally anti-phased with the 405-kyr eccentricity cycle extracted from the induration record, indicating that sea-level is the highest during eccentricity maxima.

4.2 Astronomical cycles pacing the Ludfordian carbon cycle

The new age model enables the quantification of the rate of change in the δ¹³Ccarb record, yielding maximum values of 0.09‰ per kyr for the Lau Event. These rates were derived from a LOWESS regression of the δ¹³Ccarb curve to minimise the influence of short-term local noise. As such, the values represent robust regional and potentially global benchmarks for the Lau event. The high rate of change in the δ¹³Ccarb record (0.09‰ per kyr) during the Lau Event suggests that the Silurian carbon cycle was highly dynamic during the Lau Event. Since it is now possible to constrain the rate of change, it will be possible to model the Lau biogeochemical event in the domain. This will enable us to examine whether models, such as those of Frýda et al. (2020) till hold or whether alternative models that allow for non-steady behaviour need to be used. The 100-kyr eccentricity imprint in the δ¹³Ccarb rate-of-change record during the Lau Event indicates that the carbon cycle was at least partly paced by this cycle. The phase relationship shows that the highest rates of change occurred during eccentricity maxima. This suggests that eccentricity maxima resulted in enhanced organic carbon burial during the Lau Event. The observed phase relationship aligns with a scenario in which eccentricity maxima increased the amplitude of climate precession, resulting in enhanced seasonality (Rossignol-Strick, 1985; Van Os et al., 1994; Sachs and Repeta, 1999; Wang, 2009; Gambacorta et al., 2018). The resulting increase in seasonality intensified the hydrological cycle, increasing physical and chemical weathering, supplying fine-grained sediments and nutrients through enhanced (monsoonal) rainfall. Increased fluvial discharge promoted seasonal productivity blooms, facilitating the accumulation of organic-rich deposits. In contrast, during eccentricity minima, the reduced precession amplitude weakened monsoon intensity, lowered nutrient delivery, and re-established more oxic conditions.

4.3 Astronomical pacing the lag-1 sea-level curve

The lag-1 curve used as a sea-level proxy aligns with previous sea-level interpretation made for the Lau Biogeochemical Event (Manda and Kříž, 2006; Frýda et al., 2020) (see figure 10). The Lau Event is associated with a large regression and a period of low sea-level (see figure 10). The Lau Event is associated with a large regression and a period of low sea-level (see figure 10). The lag-1 curve indicates that a large regression occurred during the Lau which is in-line with previous interpretations which inferred that the Lau Event resulted in the Mid-Ludfordian Glaciation (Frýda et al., 2020). The 405-kyr eccentricity cycle extracted from the rate lag-1 record is generally anti-phased with that from the induration record, indicating that sea level is highest during eccentricity maxima (Figure 10). The relationship between astronomical forcing and sea-level is much akin to that observed when sea-level was under a glacio-eustatic regime (Lourens and Hilgen, 1997; Li et al., 2018). The transition from relatively stable low-amplitude variability into pronounced eccentricity-paced oscillations during the Lau Event further underscores the coupling between astronomical forcing, glacio-eustatic change, and the global carbon cycle during the Lau Event. The increased amplitude of sea-level variability indicates that astronomically paced glacio-eustatic fluctuations were amplified by the climatic changes associated with the Lau Event, culminating in the Mid-Ludfordian Glaciation.

5. Conclusions

The new astrochronology from the Kosov Quarry provides the first high-resolution temporal framework for the Lau biogeochemical Event. Cyclostratigraphic analysis demonstrates that the Lau biogeochemical Event started at 424.06 ± 0.55 Ma and lasted 0.87 ± 0.03 Myr, while the associated LKB Event was much shorter at 30 ± 10 kyr. Astronomical forcing is strongly imprinted across multiple proxies. The induration record is imprinted by astronomical cycles ranging from the 9-kyr half-precession to the 405-kyr eccentricity cycle. The δ¹³Ccarb rate-of-change record contains a strong imprint of the 100-kyr eccentricity cycle, whereas the lag-1 sea-level curve displays a moderate imprint by the 405-kyr eccentricity cycle. The δ¹³Ccarb rate-of-change curve further documents maximum values of 0.09 ‰/kyr during the onset of the Lau, highlighting the highly dynamic character of the Silurian carbon cycle. The imprint of the 100-kyr eccentricity cycle in the δ¹³Ccarb rate-of-change also indicates that this cycle acted as a pacemaker of carbon cycle variability and carbon burial during the Lau Event. The strong imprint of the 405-kyr eccentricity cycle in the lag-1 sea-level and the inferred phase relationship indicate that sea-level was subjected to an astronomically paced glacio-eustatic regime. Together, the results indicate that astronomical forcing amplified feedbacks between climate, organic carbon burial, and glacio-eustasy, shaping the magnitude and duration of the Lau Event. More broadly, these findings suggest that eccentricity-paced mechanisms played a pivotal role is shaping the climate, sea-level and carbon cycle dynamics during the Silurian.

6. References

Arts, M., Corradini, C., Pondrelli, M., Pas, D., and Da Silva, A.C., 2024, Age and orbital forcing in the upper Silurian Cellon section (Carnic Alps, Austria) uncovered using the WaverideR R package: Frontiers in Earth Science, v. 12, p. 1–24, doi:10.3389/feart.2024.1357751.
Farhat, M., Auclair-Desrotour, P., Boué, G., and Laskar, J., 2022, The resonant tidal evolution of the Earth-Moon distance: Astronomy & Astrophysics, v. 665, p. L1.
Fatka, O., and Mergl, M., 2009, The “microcontinent” Perunica: Status and story 15 years after conception: Geological Society, London, Special Publications, v. 325, p. 65–101, doi:10.1144/SP325.4.
Frýda, J., Lehnert, O., Frýdová, B., Farkaš, J., and Kubajko, M., 2020, Carbon and sulfur cycling during the mid-Ludfordian anomaly and the linkage with the late Silurian Lau/Kozlowskii Bioevent: Palaeogeography, Palaeoclimatology, Palaeoecology, v. 564, p. 110152, doi:10.1016/j.palaeo.2020.110152.
Frýda, J., Lehnert, O., Joachimski, M.M., Männik, P., Kubajko, M., Mergl, M., Farkaš, J., and Frýdová, B., 2021, The Mid-Ludfordian (late Silurian) Glaciation: A link with global changes in ocean chemistry and ecosystem overturns: Earth-Science Reviews, v. 220, p. 103652, doi:10.1016/j.earscirev.2021.103652.
Frýda, J., and Manda, Š., 2013, A long-lasting steady period of isotopically heavy carbon in the late Silurian ocean: Evolution of the &delta;13 record and its significance for an integrated &delta;13, graptolite and conodont stratigraphy: Bulletin of Geosciences, p. 463–482, doi:10.3140/bull.geosci.1436.
Gambacorta, G., Menichetti, E., Trincianti, E., and Torricelli, S., 2018, Orbital control on cyclical primary productivity and benthic anoxia: Astronomical tuning of the Telychian Stage (Early Silurian): Palaeogeography, Palaeoclimatology, Palaeoecology, v. 495, p. 152–162, doi:10.1016/j.palaeo.2018.01.003.
Hilgen, F., Zeeden, C., and Laskar, J., 2020, Paleoclimate records reveal elusive 200-kyr eccentricity cycle for the first time: Global and Planetary Change, v. 194, p. 103296, doi:10.1016/j.gloplacha.2020.103296.
Hinnov, L.A., 2000, New Perspectives on Orbitally Forced Stratigraphy: Annual Review of Earth and Planetary Sciences, v. 28, p. 419–475, doi:10.1177/0741713604268894.
HORNÝ, R., 1955, Base vrstev kopaninských eß 1 na jihozápadním okraji vulkanické facie (Kosov u Berouna): Věstník Ústředního ústavu geologického, v. 30, p. 81–86.
Kříž, J., 1992, Silurian field excursions: Prague Basin (Barrandian), Bohemia: National Museum of Wales, v. 13.
Kříž, J., 1991, The Silurian of the Prague Basin (Bohemia)–tectonic, eustatic and volcanic controls on facies and faunal development: Special Papers in Palaeontology, v. 44, p. 179–203.
Laskar, J., 2020, Astrochronology, in Geologic Time Scale 2020, Elsevier, v. 1774, p. 139–158, doi:10.1016/B978-0-12-824360-2.00004-8.
Laskar, J., Robutel, P., Joutel, F., Gastineau, M., Correia, a.C.M., and Levrard, B., 2004, A long-term numerical solution for the insolation quantities of the Earth: Astronomy and Astrophysics, v. 428, p. 261–285, doi:10.1051/0004-6361:20041335.
Lehnert, O., Frýda, J., Buggisch, W., Munnecke, A., Nützel, A., Křiž, J., and Manda, S., 2007, δ13C records across the late Silurian Lau event: New data from middle palaeo-latitudes of northern peri-Gondwana (Prague Basin, Czech Republic): Palaeogeography, Palaeoclimatology, Palaeoecology, v. 245, p. 227–244, doi:10.1016/j.palaeo.2006.02.022.
Li, M., Hinnov, L.A., Huang, C., and Ogg, J.G., 2018, Sedimentary noise and sea levels linked to land-ocean water exchange and obliquity forcing: Nature Communications, v. 9, doi:10.1038/s41467-018-03454-y.
Lourens, L.J., and Hilgen, F.J., 1997, Long-periodic variations in the earth’s obliquity and their relation to third-order eustatic cycles and late Neogene glaciations: Quaternary International, v. 40, p. 43–52, doi:10.1016/S1040-6182(96)00060-2.
Manda, Š., and Kříž, J., 2006, Environmental and biotic changes in subtropical isolated carbonate platforms during the Late Silurian Kozlowskii Event, Prague Basin: GFF, v. 128, p. 161–168.
Martinez, M., 2018, Mechanisms of Preservation of the Eccentricity and Longer-term Milankovitch Cycles in Detrital Supply and Carbonate Production in Hemipelagic Marl-Limestone Alternations: Elsevier Ltd, v. 3, 189–218 p., doi:10.1016/bs.sats.2018.08.002.
Meyers, S.R., 2019, Cyclostratigraphy and the problem of astrochronologic testing: Earth-Science Reviews, v. 190, p. 190–223, doi:10.1016/j.earscirev.2018.11.015.
Meyers, S.R., 2015, The evaluation of eccentricity-related amplitude modulation and bundling in paleoclimate data: An inverse approach for astrochronologic testing and time scale optimization: Paleoceanography, v. 30, p. 1625–1640, doi:10.1002/2015PA002850.
Mutterlose, J., and Ruffell, A., 1999, Milankovitch-scale palaeoclimate changes in pale-dark bedding rhythms from the Early Cretaceous (Hauterivian and Barremian) of eastern England and northern Germany: Palaeogeography, Palaeoclimatology, Palaeoecology, v. 154, p. 133–160, doi:10.1016/S0031-0182(99)00107-8.
Rossignol-Strick, M., 1985, MEDITERRANEAN QUATERNARY SAPROPELS, AN IMMEDIATE RESPONSE OF THE AFRICAN MONSOON TO VARIATION OF INSOLATION: Elsevier Science Publishers B.V, v. 49, p. 237–263, http://eesc.ldeo.columbia.edu/courses/w4937/Readings/Rossignol_Strick.1985.pdf.
Sachs, J.P., and Repeta, D.J., 1999, Oligotrophy and nitrogen fixation during eastern Mediterranean sapropel events: Science, v. 286, p. 2485–2488.
Scotese, C.R., 2021, An atlas of phanerozoic paleogeographic maps: The seas come in and the seas go out: Annual Review of Earth and Planetary Sciences, v. 49, p. 679–728, doi:10.1146/annurev-earth-081320-064052.
Štorch, P., 1995, Biotic crises and post-crisis recoveries recorded by Silurian planktonic graptolite faunas of the Barrandian area (Czech Republic): Geolines, v. 3, p. 59–70.
Tasáryová, Z., Schnabl, P., Čížková, K., Pruner, P., Janoušek, V., Rapprich, V., Štorch, P., Manda, Š., Frýda, J., and Trubač, J., 2014, Gorstian palaeoposition and geotectonic setting of Suchomasty Volcanic Centre (Silurian, Prague Basin, Teplá-Barrandian Unit, Bohemian Massif): Gff, v. 136, p. 262–265, doi:10.1080/11035897.2013.879735.
Van Os, B., Lourens, L., Hilgen, F., De Lange, G., and Beaufort, L., 1994, The formation of Pliocene sapropels and carbonate cycles in the Mediterranean: Diagenesis, dilution, and productivity: Paleoceanography, v. 9, p. 601–617.
Waltham, D., 2015, Milankovitch Period Uncertainties and Their Impact On Cyclostratigraphy: Journal of Sedimentary Research, v. 85, p. 990–998, doi:10.2110/jsr.2015.66.
Wang, P., 2009, Global monsoon in a geological perspective: Chinese Science Bulletin, v. 54, p. 1113–1136.
Wu, Y., Malinverno, A., Meyers, S.R., and Hinnov, L.A., 2024, A 650-Myr history of Earth’s axial precession frequency and the evolution of the Earth-Moon system derived from cyclostratigraphy: Science Advances, v. 10, p. eado2412.